clerk-sdk 0.5.2__py3-none-any.whl → 0.5.4__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.
- clerk/__init__.py +1 -1
- clerk/development/cli.py +57 -4
- clerk/development/code_runner.py +384 -0
- clerk/development/gui/graph_checker.py +216 -0
- clerk/development/gui/test_session.py +3 -3
- clerk/development/init_project.py +22 -0
- clerk/development/templates/launch.json.template +20 -0
- clerk/development/templates/main_gui.py.template +3 -3
- clerk/development/templates/tasks.json.template +25 -0
- clerk/development/templates/test_payload.py.template +32 -0
- clerk/gui_automation/client_actor/client_actor.py +1 -1
- {clerk_sdk-0.5.2.dist-info → clerk_sdk-0.5.4.dist-info}/METADATA +2 -1
- {clerk_sdk-0.5.2.dist-info → clerk_sdk-0.5.4.dist-info}/RECORD +16 -11
- {clerk_sdk-0.5.2.dist-info → clerk_sdk-0.5.4.dist-info}/WHEEL +0 -0
- {clerk_sdk-0.5.2.dist-info → clerk_sdk-0.5.4.dist-info}/entry_points.txt +0 -0
- {clerk_sdk-0.5.2.dist-info → clerk_sdk-0.5.4.dist-info}/licenses/LICENSE +0 -0
clerk/__init__.py
CHANGED
clerk/development/cli.py
CHANGED
|
@@ -53,26 +53,58 @@ def main():
|
|
|
53
53
|
help="GUI automation commands"
|
|
54
54
|
)
|
|
55
55
|
gui_subparsers = gui_parser.add_subparsers(dest="gui_command", help="GUI subcommands")
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
# GUI connect subcommand
|
|
58
58
|
gui_connect_parser = gui_subparsers.add_parser(
|
|
59
59
|
"connect",
|
|
60
60
|
help="Start interactive GUI automation test session"
|
|
61
61
|
)
|
|
62
62
|
|
|
63
|
+
# GUI graph check subcommand
|
|
64
|
+
gui_graph_parser = gui_subparsers.add_parser(
|
|
65
|
+
"graph", help="Graph analysis commands"
|
|
66
|
+
)
|
|
67
|
+
gui_graph_subparsers = gui_graph_parser.add_subparsers(
|
|
68
|
+
dest="graph_command", help="Graph subcommands"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
gui_graph_check_parser = gui_graph_subparsers.add_parser(
|
|
72
|
+
"check", help="Check and visualize state machine graph structure"
|
|
73
|
+
)
|
|
74
|
+
gui_graph_check_parser.add_argument(
|
|
75
|
+
"--module-path",
|
|
76
|
+
type=str,
|
|
77
|
+
required=False,
|
|
78
|
+
default=None,
|
|
79
|
+
help="Path to the Python file containing the state machine (defaults to src/main.py)",
|
|
80
|
+
)
|
|
81
|
+
|
|
63
82
|
# Schema command group
|
|
64
83
|
schema_parser = subparsers.add_parser(
|
|
65
84
|
"schema",
|
|
66
85
|
help="Schema management commands"
|
|
67
86
|
)
|
|
68
87
|
schema_subparsers = schema_parser.add_subparsers(dest="schema_command", help="Schema subcommands")
|
|
69
|
-
|
|
88
|
+
|
|
70
89
|
# Schema fetch subcommand
|
|
71
90
|
schema_fetch_parser = schema_subparsers.add_parser(
|
|
72
91
|
"fetch",
|
|
73
92
|
help="Fetch and generate Pydantic models from project schema"
|
|
74
93
|
)
|
|
75
94
|
|
|
95
|
+
# Code command group
|
|
96
|
+
code_parser = subparsers.add_parser(
|
|
97
|
+
"code", help="Custom code development and testing commands"
|
|
98
|
+
)
|
|
99
|
+
code_subparsers = code_parser.add_subparsers(
|
|
100
|
+
dest="code_command", help="Code subcommands"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Code run subcommand
|
|
104
|
+
code_run_parser = code_subparsers.add_parser(
|
|
105
|
+
"run", help="Run custom code with test payloads"
|
|
106
|
+
)
|
|
107
|
+
|
|
76
108
|
args = parser.parse_args()
|
|
77
109
|
|
|
78
110
|
# Show help if no command specified
|
|
@@ -90,16 +122,27 @@ def main():
|
|
|
90
122
|
if not hasattr(args, 'gui_command') or not args.gui_command:
|
|
91
123
|
gui_parser.print_help()
|
|
92
124
|
sys.exit(1)
|
|
93
|
-
|
|
125
|
+
|
|
94
126
|
if args.gui_command == "connect":
|
|
95
127
|
from clerk.development.gui.test_session import main as gui_main
|
|
96
128
|
gui_main()
|
|
97
129
|
|
|
130
|
+
elif args.gui_command == "graph":
|
|
131
|
+
if not hasattr(args, "graph_command") or not args.graph_command:
|
|
132
|
+
print("Error: graph command requires a subcommand")
|
|
133
|
+
print("Available subcommands: check")
|
|
134
|
+
sys.exit(1)
|
|
135
|
+
|
|
136
|
+
if args.graph_command == "check":
|
|
137
|
+
from clerk.development.gui.graph_checker import check_graph
|
|
138
|
+
|
|
139
|
+
check_graph(args.module_path)
|
|
140
|
+
|
|
98
141
|
elif args.command == "schema":
|
|
99
142
|
if not hasattr(args, 'schema_command') or not args.schema_command:
|
|
100
143
|
schema_parser.print_help()
|
|
101
144
|
sys.exit(1)
|
|
102
|
-
|
|
145
|
+
|
|
103
146
|
if args.schema_command == "fetch":
|
|
104
147
|
from clerk.development.schema.fetch_schema import main_with_args
|
|
105
148
|
project_id = os.getenv("PROJECT_ID")
|
|
@@ -108,6 +151,16 @@ def main():
|
|
|
108
151
|
sys.exit(1)
|
|
109
152
|
main_with_args(project_id, project_root)
|
|
110
153
|
|
|
154
|
+
elif args.command == "code":
|
|
155
|
+
if not hasattr(args, "code_command") or not args.code_command:
|
|
156
|
+
code_parser.print_help()
|
|
157
|
+
sys.exit(1)
|
|
158
|
+
|
|
159
|
+
if args.code_command == "run":
|
|
160
|
+
from clerk.development.code_runner import main_with_args
|
|
161
|
+
|
|
162
|
+
main_with_args(project_root)
|
|
163
|
+
|
|
111
164
|
|
|
112
165
|
if __name__ == "__main__":
|
|
113
166
|
main()
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
"""Code runner module for testing custom code with payloads."""
|
|
2
|
+
import json
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
|
+
from importlib import import_module
|
|
7
|
+
import importlib.util
|
|
8
|
+
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.panel import Panel
|
|
11
|
+
from rich.prompt import Confirm, Prompt
|
|
12
|
+
from rich import print as rprint
|
|
13
|
+
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _generate_structured_data_code(structured_data_class) -> str:
|
|
18
|
+
"""Generate code for StructuredData initialization with all fields.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
structured_data_class: The StructuredData class from schema
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
String with indented field assignments
|
|
25
|
+
"""
|
|
26
|
+
from typing import get_origin, get_args
|
|
27
|
+
from pydantic import BaseModel
|
|
28
|
+
|
|
29
|
+
lines = []
|
|
30
|
+
|
|
31
|
+
# Get model fields
|
|
32
|
+
if hasattr(structured_data_class, 'model_fields'):
|
|
33
|
+
fields = structured_data_class.model_fields
|
|
34
|
+
|
|
35
|
+
for field_name, field_info in fields.items():
|
|
36
|
+
annotation = field_info.annotation
|
|
37
|
+
|
|
38
|
+
# Check if it's a List type
|
|
39
|
+
origin = get_origin(annotation)
|
|
40
|
+
if origin is list:
|
|
41
|
+
lines.append(f" {field_name}=[],")
|
|
42
|
+
# Check if it's an Optional type
|
|
43
|
+
elif origin is type(None) or (hasattr(annotation, '__origin__') and annotation.__origin__ is type(None)):
|
|
44
|
+
lines.append(f" {field_name}=None,")
|
|
45
|
+
# Check if the annotation is a BaseModel subclass
|
|
46
|
+
else:
|
|
47
|
+
# Try to check if it's a BaseModel (handle Optional types)
|
|
48
|
+
actual_type = annotation
|
|
49
|
+
if origin:
|
|
50
|
+
# For Optional[Type], get the actual type
|
|
51
|
+
args = get_args(annotation)
|
|
52
|
+
if args:
|
|
53
|
+
# Filter out NoneType
|
|
54
|
+
non_none_args = [arg for arg in args if arg is not type(None)]
|
|
55
|
+
if non_none_args:
|
|
56
|
+
actual_type = non_none_args[0]
|
|
57
|
+
|
|
58
|
+
# Check if actual_type is a class and subclass of BaseModel
|
|
59
|
+
try:
|
|
60
|
+
if isinstance(actual_type, type) and issubclass(actual_type, BaseModel):
|
|
61
|
+
class_name = actual_type.__name__
|
|
62
|
+
lines.append(f" {field_name}={class_name}(),")
|
|
63
|
+
else:
|
|
64
|
+
lines.append(f" {field_name}=None,")
|
|
65
|
+
except (TypeError, AttributeError):
|
|
66
|
+
lines.append(f" {field_name}=None,")
|
|
67
|
+
|
|
68
|
+
return "\n".join(lines)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def find_test_payloads(project_root: Path) -> list[Path]:
|
|
72
|
+
"""Find all test payload Python files in test/payloads directory.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
project_root: Project root directory
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
List of Path objects for payload files
|
|
79
|
+
"""
|
|
80
|
+
payload_dir = project_root / "test" / "payloads"
|
|
81
|
+
|
|
82
|
+
if not payload_dir.exists():
|
|
83
|
+
return []
|
|
84
|
+
|
|
85
|
+
# Find all .py files except __init__.py
|
|
86
|
+
return [p for p in payload_dir.glob("*.py") if p.name != "__init__.py"]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def create_test_payload_template(project_root: Path) -> Path:
|
|
90
|
+
"""Create a template test payload Python file.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
project_root: Project root directory
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Path to the created template file
|
|
97
|
+
"""
|
|
98
|
+
payload_dir = project_root / "test" / "payloads"
|
|
99
|
+
payload_dir.mkdir(parents=True, exist_ok=True)
|
|
100
|
+
|
|
101
|
+
# Check if schema exists
|
|
102
|
+
schema_path = project_root / "src" / "schema.py"
|
|
103
|
+
if not schema_path.exists():
|
|
104
|
+
console.print("[red]x[/red] No schema found. Run 'clerk schema fetch' first.")
|
|
105
|
+
console.print("[dim]Cannot generate test payload without schema.[/dim]")
|
|
106
|
+
sys.exit(1)
|
|
107
|
+
|
|
108
|
+
console.print("[green]✓[/green] Found schema at src/schema.py")
|
|
109
|
+
|
|
110
|
+
# Load schema to generate structured data template
|
|
111
|
+
structured_data_code = None
|
|
112
|
+
try:
|
|
113
|
+
# Add src to path
|
|
114
|
+
src_path = str(project_root / "src")
|
|
115
|
+
if src_path not in sys.path:
|
|
116
|
+
sys.path.insert(0, src_path)
|
|
117
|
+
|
|
118
|
+
spec = importlib.util.spec_from_file_location("schema", schema_path)
|
|
119
|
+
if spec and spec.loader:
|
|
120
|
+
schema_module = importlib.util.module_from_spec(spec)
|
|
121
|
+
spec.loader.exec_module(schema_module)
|
|
122
|
+
|
|
123
|
+
if hasattr(schema_module, "StructuredData"):
|
|
124
|
+
structured_data_class = getattr(schema_module, "StructuredData")
|
|
125
|
+
# Generate code with all fields
|
|
126
|
+
structured_data_code = _generate_structured_data_code(structured_data_class)
|
|
127
|
+
except Exception as e:
|
|
128
|
+
console.print(f"[red]x[/red] Could not load schema: {str(e)}")
|
|
129
|
+
sys.exit(1)
|
|
130
|
+
|
|
131
|
+
if not structured_data_code:
|
|
132
|
+
console.print("[red]x[/red] Could not generate structured data code from schema.")
|
|
133
|
+
sys.exit(1)
|
|
134
|
+
|
|
135
|
+
# Get name from user
|
|
136
|
+
name = Prompt.ask(
|
|
137
|
+
"Enter a name for this test payload",
|
|
138
|
+
default="test_payload_1"
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Ensure .py extension
|
|
142
|
+
if not name.endswith(".py"):
|
|
143
|
+
name = f"{name}.py"
|
|
144
|
+
|
|
145
|
+
payload_path = payload_dir / name
|
|
146
|
+
|
|
147
|
+
# Load and populate template
|
|
148
|
+
template_dir = Path(__file__).parent / "templates"
|
|
149
|
+
template_path = template_dir / "test_payload.py.template"
|
|
150
|
+
template_code = template_path.read_text(encoding="utf-8")
|
|
151
|
+
# Replace placeholder with actual fields
|
|
152
|
+
template_code = template_code.replace("{structured_data_fields}", structured_data_code)
|
|
153
|
+
|
|
154
|
+
# Write the template
|
|
155
|
+
with open(payload_path, "w", encoding="utf-8") as f:
|
|
156
|
+
f.write(template_code)
|
|
157
|
+
|
|
158
|
+
console.print(f"\n[green]✓[/green] Created template payload: {payload_path}")
|
|
159
|
+
console.print("\n[yellow]Please edit this file to customize your test data before continuing.[/yellow]")
|
|
160
|
+
|
|
161
|
+
return payload_path
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def select_payload(payloads: list[Path]) -> Path:
|
|
165
|
+
"""Let user select a payload by number.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
payloads: List of payload file paths
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
Selected payload path
|
|
172
|
+
"""
|
|
173
|
+
console.print("\n[bold]Available test payloads:[/bold]")
|
|
174
|
+
for i, payload in enumerate(payloads, 1):
|
|
175
|
+
console.print(f" [cyan]{i}[/cyan]. {payload.stem}")
|
|
176
|
+
|
|
177
|
+
while True:
|
|
178
|
+
try:
|
|
179
|
+
choice = Prompt.ask(
|
|
180
|
+
"\nSelect a payload",
|
|
181
|
+
default="1"
|
|
182
|
+
)
|
|
183
|
+
idx = int(choice) - 1
|
|
184
|
+
if 0 <= idx < len(payloads):
|
|
185
|
+
return payloads[idx]
|
|
186
|
+
else:
|
|
187
|
+
console.print(f"[red]Please enter a number between 1 and {len(payloads)}[/red]")
|
|
188
|
+
except ValueError:
|
|
189
|
+
console.print("[red]Please enter a valid number[/red]")
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def load_payload(payload_path: Path, project_root: Path):
|
|
193
|
+
"""Load payload from Python module.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
payload_path: Path to payload Python file
|
|
197
|
+
project_root: Project root directory
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
ClerkCodePayload object
|
|
201
|
+
"""
|
|
202
|
+
# Add project root and src to path so imports work
|
|
203
|
+
project_root_str = str(project_root)
|
|
204
|
+
src_path = str(project_root / "src")
|
|
205
|
+
|
|
206
|
+
if project_root_str not in sys.path:
|
|
207
|
+
sys.path.insert(0, project_root_str)
|
|
208
|
+
if src_path not in sys.path:
|
|
209
|
+
sys.path.insert(0, src_path)
|
|
210
|
+
|
|
211
|
+
# Load the payload module
|
|
212
|
+
spec = importlib.util.spec_from_file_location(
|
|
213
|
+
f"test_payload_{payload_path.stem}",
|
|
214
|
+
payload_path
|
|
215
|
+
)
|
|
216
|
+
if not spec or not spec.loader:
|
|
217
|
+
raise ImportError(f"Could not load payload module from {payload_path}")
|
|
218
|
+
|
|
219
|
+
payload_module = importlib.util.module_from_spec(spec)
|
|
220
|
+
spec.loader.exec_module(payload_module)
|
|
221
|
+
|
|
222
|
+
# Get the payload object
|
|
223
|
+
if not hasattr(payload_module, "payload"):
|
|
224
|
+
raise AttributeError(f"Payload module must define a 'payload' variable")
|
|
225
|
+
|
|
226
|
+
return payload_module.payload
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def run_main_with_payload(project_root: Path, payload_path: Path):
|
|
230
|
+
"""Run main() from src/main.py with the selected payload.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
project_root: Project root directory
|
|
234
|
+
payload_path: Path to the payload Python file
|
|
235
|
+
"""
|
|
236
|
+
console.print()
|
|
237
|
+
console.print(Panel(
|
|
238
|
+
f"[bold]Running main() with payload: {payload_path.name}[/bold]",
|
|
239
|
+
style="cyan"
|
|
240
|
+
))
|
|
241
|
+
|
|
242
|
+
# Load payload
|
|
243
|
+
try:
|
|
244
|
+
payload_obj = load_payload(payload_path, project_root)
|
|
245
|
+
console.print("[green]✓[/green] Loaded payload")
|
|
246
|
+
except Exception as e:
|
|
247
|
+
console.print(f"[red]x[/red] Failed to load payload: {str(e)}")
|
|
248
|
+
import traceback
|
|
249
|
+
console.print("[dim]" + traceback.format_exc() + "[/dim]")
|
|
250
|
+
sys.exit(1)
|
|
251
|
+
|
|
252
|
+
# Find main.py
|
|
253
|
+
main_path = project_root / "src" / "main.py"
|
|
254
|
+
if not main_path.exists():
|
|
255
|
+
console.print(f"[red]x[/red] main.py not found at {main_path}")
|
|
256
|
+
sys.exit(1)
|
|
257
|
+
|
|
258
|
+
# Add src to path
|
|
259
|
+
src_path = str(project_root / "src")
|
|
260
|
+
if src_path not in sys.path:
|
|
261
|
+
sys.path.insert(0, src_path)
|
|
262
|
+
|
|
263
|
+
# Start debugpy server and wait for VS Code to attach
|
|
264
|
+
import debugpy
|
|
265
|
+
|
|
266
|
+
debug_port = 5678
|
|
267
|
+
|
|
268
|
+
# Check if already running under debugger
|
|
269
|
+
if not debugpy.is_client_connected():
|
|
270
|
+
console.print(f"\n[cyan]Starting debug server on port {debug_port}...[/cyan]")
|
|
271
|
+
debugpy.listen(("localhost", debug_port))
|
|
272
|
+
|
|
273
|
+
console.print()
|
|
274
|
+
console.print("[bold yellow]⚡ Ready for debugging![/bold yellow]")
|
|
275
|
+
console.print()
|
|
276
|
+
console.print("[bold]To start debugging:[/bold]")
|
|
277
|
+
console.print(" [cyan]→ Press F5 in VS Code[/cyan]")
|
|
278
|
+
console.print(" [dim]or select 'Clerk: Debug Code Run' from the debug panel[/dim]")
|
|
279
|
+
console.print()
|
|
280
|
+
console.print("[dim]Press Ctrl+C to skip debugging and run without debugger[/dim]\n")
|
|
281
|
+
|
|
282
|
+
try:
|
|
283
|
+
debugpy.wait_for_client()
|
|
284
|
+
console.print("[green]✓[/green] Debugger attached!\n")
|
|
285
|
+
except KeyboardInterrupt:
|
|
286
|
+
console.print("\n[yellow]Skipping debugger, running without debug...[/yellow]\n")
|
|
287
|
+
else:
|
|
288
|
+
console.print("\n[green]✓[/green] Already running under debugger\n")
|
|
289
|
+
|
|
290
|
+
# Import and run
|
|
291
|
+
try:
|
|
292
|
+
console.print()
|
|
293
|
+
console.print("[bold cyan]═══════════════════════════════════════════════════════[/bold cyan]")
|
|
294
|
+
console.print("[bold cyan] Starting Execution [/bold cyan]")
|
|
295
|
+
console.print("[bold cyan]═══════════════════════════════════════════════════════[/bold cyan]")
|
|
296
|
+
console.print()
|
|
297
|
+
|
|
298
|
+
# Import main module
|
|
299
|
+
spec = importlib.util.spec_from_file_location("main", main_path)
|
|
300
|
+
if spec and spec.loader:
|
|
301
|
+
main_module = importlib.util.module_from_spec(spec)
|
|
302
|
+
spec.loader.exec_module(main_module)
|
|
303
|
+
|
|
304
|
+
# Call main
|
|
305
|
+
if hasattr(main_module, "main"):
|
|
306
|
+
# Run main with the loaded payload
|
|
307
|
+
result = main_module.main(payload_obj)
|
|
308
|
+
|
|
309
|
+
console.print()
|
|
310
|
+
console.print("[bold cyan]═══════════════════════════════════════════════════════[/bold cyan]")
|
|
311
|
+
console.print("[bold cyan] Execution Complete [/bold cyan]")
|
|
312
|
+
console.print("[bold cyan]═══════════════════════════════════════════════════════[/bold cyan]")
|
|
313
|
+
console.print()
|
|
314
|
+
|
|
315
|
+
# Show result
|
|
316
|
+
if result:
|
|
317
|
+
console.print("[bold]Result:[/bold]")
|
|
318
|
+
console.print(Panel(
|
|
319
|
+
f"Document ID: {result.document.id}\n"
|
|
320
|
+
f"Run ID: {result.run_id}",
|
|
321
|
+
title="Execution Result",
|
|
322
|
+
style="green"
|
|
323
|
+
))
|
|
324
|
+
|
|
325
|
+
# Show updated structured_data
|
|
326
|
+
if result.structured_data:
|
|
327
|
+
console.print("\n[bold]Updated Structured Data:[/bold]")
|
|
328
|
+
rprint(result.structured_data)
|
|
329
|
+
else:
|
|
330
|
+
console.print("[yellow]![/yellow] No result returned")
|
|
331
|
+
else:
|
|
332
|
+
console.print(f"[red]x[/red] No main() function found in {main_path}")
|
|
333
|
+
sys.exit(1)
|
|
334
|
+
else:
|
|
335
|
+
console.print(f"[red]x[/red] Could not load {main_path}")
|
|
336
|
+
sys.exit(1)
|
|
337
|
+
|
|
338
|
+
except Exception as e:
|
|
339
|
+
console.print()
|
|
340
|
+
console.print(f"[red]x Error during execution:[/red] {str(e)}")
|
|
341
|
+
import traceback
|
|
342
|
+
console.print("[dim]" + traceback.format_exc() + "[/dim]")
|
|
343
|
+
sys.exit(1)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def main_with_args(project_root: Path):
|
|
347
|
+
"""Main entry point for code runner.
|
|
348
|
+
|
|
349
|
+
Args:
|
|
350
|
+
project_root: Project root directory
|
|
351
|
+
"""
|
|
352
|
+
console.print()
|
|
353
|
+
console.print(Panel(
|
|
354
|
+
"[bold]Clerk Code Runner[/bold]\n"
|
|
355
|
+
"Run your custom code with test payloads",
|
|
356
|
+
style="cyan"
|
|
357
|
+
))
|
|
358
|
+
|
|
359
|
+
# Find payloads
|
|
360
|
+
payloads = find_test_payloads(project_root)
|
|
361
|
+
|
|
362
|
+
if not payloads:
|
|
363
|
+
console.print("\n[yellow]No test payloads found in test/payloads/[/yellow]")
|
|
364
|
+
|
|
365
|
+
if Confirm.ask("Would you like to generate a template payload?", default=True):
|
|
366
|
+
payload_path = create_test_payload_template(project_root)
|
|
367
|
+
|
|
368
|
+
console.print("\n[bold]Next steps:[/bold]")
|
|
369
|
+
console.print(f"1. Edit {payload_path} with your test data")
|
|
370
|
+
console.print("2. Run [cyan]clerk code run[/cyan] again to execute")
|
|
371
|
+
return
|
|
372
|
+
else:
|
|
373
|
+
console.print("\n[dim]Create a Python file in test/payloads/ and run again.[/dim]")
|
|
374
|
+
return
|
|
375
|
+
|
|
376
|
+
# Show available payloads
|
|
377
|
+
console.print(f"\n[green]✓[/green] Found {len(payloads)} test payload(s)")
|
|
378
|
+
|
|
379
|
+
# Let user select
|
|
380
|
+
selected_payload = select_payload(payloads)
|
|
381
|
+
console.print(f"\n[green]→[/green] Selected: {selected_payload.name}")
|
|
382
|
+
|
|
383
|
+
# Run with selected payload
|
|
384
|
+
run_main_with_payload(project_root, selected_payload)
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""Graph checker utility for ScreenPilot state machines"""
|
|
2
|
+
import sys
|
|
3
|
+
import importlib.util
|
|
4
|
+
import logging
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Set, List, Optional
|
|
7
|
+
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.panel import Panel
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
|
|
12
|
+
console = Console()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ErrorCapturingHandler(logging.Handler):
|
|
16
|
+
"""Custom logging handler to capture error messages during module load"""
|
|
17
|
+
|
|
18
|
+
def __init__(self):
|
|
19
|
+
super().__init__()
|
|
20
|
+
self.errors: List[str] = []
|
|
21
|
+
self.setLevel(logging.ERROR)
|
|
22
|
+
|
|
23
|
+
def emit(self, record):
|
|
24
|
+
if "involves undefined state" in record.getMessage():
|
|
25
|
+
self.errors.append(record.getMessage())
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def load_module_from_path(module_path: str) -> ErrorCapturingHandler:
|
|
29
|
+
"""
|
|
30
|
+
Load a Python module from a file path.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
module_path: Path to the Python file containing the state machine
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
ErrorCapturingHandler with any errors captured during load
|
|
37
|
+
"""
|
|
38
|
+
path = Path(module_path).resolve()
|
|
39
|
+
if not path.exists():
|
|
40
|
+
raise FileNotFoundError(f"Module not found: {module_path}")
|
|
41
|
+
|
|
42
|
+
# Set up error capturing handler
|
|
43
|
+
error_handler = ErrorCapturingHandler()
|
|
44
|
+
logger = logging.getLogger("state_machine.py")
|
|
45
|
+
logger.addHandler(error_handler)
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
# Add the parent directory to sys.path for relative imports
|
|
49
|
+
parent_dir = str(path.parent)
|
|
50
|
+
if parent_dir not in sys.path:
|
|
51
|
+
sys.path.insert(0, parent_dir)
|
|
52
|
+
|
|
53
|
+
# Load the module
|
|
54
|
+
spec = importlib.util.spec_from_file_location(path.stem, path)
|
|
55
|
+
if spec is None or spec.loader is None:
|
|
56
|
+
raise ImportError(f"Could not load module from {module_path}")
|
|
57
|
+
|
|
58
|
+
module = importlib.util.module_from_spec(spec)
|
|
59
|
+
sys.modules[path.stem] = module
|
|
60
|
+
|
|
61
|
+
# Capture ValueError exceptions during module execution
|
|
62
|
+
try:
|
|
63
|
+
spec.loader.exec_module(module)
|
|
64
|
+
except ValueError as e:
|
|
65
|
+
error_msg = str(e)
|
|
66
|
+
if "already registered" in error_msg or "provide a condition function" in error_msg:
|
|
67
|
+
# This is a fatal error - the transition can't be registered
|
|
68
|
+
# Re-raise it so we can show it properly
|
|
69
|
+
raise ValueError(f"Duplicate transition error: {error_msg}") from e
|
|
70
|
+
else:
|
|
71
|
+
raise
|
|
72
|
+
finally:
|
|
73
|
+
# Clean up handler
|
|
74
|
+
logger.removeHandler(error_handler)
|
|
75
|
+
|
|
76
|
+
return error_handler
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def check_graph(module_path: Optional[str] = None):
|
|
80
|
+
"""
|
|
81
|
+
Main function to check a ScreenPilot state machine graph.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
module_path: Path to the Python file containing the state machine.
|
|
85
|
+
If None, defaults to src/main.py in current directory.
|
|
86
|
+
"""
|
|
87
|
+
try:
|
|
88
|
+
# Clear any existing graph state from ScreenPilot before loading
|
|
89
|
+
from clerk.gui_automation.ui_state_machine import ScreenPilot
|
|
90
|
+
import networkx as nx
|
|
91
|
+
ScreenPilot._graph = nx.MultiDiGraph()
|
|
92
|
+
|
|
93
|
+
# Default to src/main.py if no path provided
|
|
94
|
+
if module_path is None:
|
|
95
|
+
default_path = Path.cwd() / "src" / "main.py"
|
|
96
|
+
if not default_path.exists():
|
|
97
|
+
console.print(f"\n[red]Error: Default file not found: {default_path}[/red]")
|
|
98
|
+
console.print("[dim]Specify --module-path to check a different file[/dim]")
|
|
99
|
+
sys.exit(1)
|
|
100
|
+
module_path = str(default_path)
|
|
101
|
+
console.print(f"[dim]Using default: {module_path}[/dim]")
|
|
102
|
+
|
|
103
|
+
# Load the module (this will trigger state/transition registrations)
|
|
104
|
+
console.print(f"\n[dim]Loading module: {module_path}[/dim]")
|
|
105
|
+
error_handler = load_module_from_path(module_path)
|
|
106
|
+
|
|
107
|
+
graph = ScreenPilot._graph
|
|
108
|
+
|
|
109
|
+
if len(graph.nodes()) == 0:
|
|
110
|
+
console.print("\n[yellow]⚠️ Warning: No states found in the graph.[/yellow]")
|
|
111
|
+
console.print("[dim]Make sure your module imports and registers states using decorators[/dim]")
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
# Count states and transitions
|
|
115
|
+
state_count = len(graph.nodes())
|
|
116
|
+
transition_count = len(graph.edges())
|
|
117
|
+
|
|
118
|
+
# Show statistics
|
|
119
|
+
console.print()
|
|
120
|
+
stats_table = Table(show_header=False, box=None, padding=(0, 2))
|
|
121
|
+
stats_table.add_column(style="cyan bold")
|
|
122
|
+
stats_table.add_column(style="white")
|
|
123
|
+
stats_table.add_row("States:", str(state_count))
|
|
124
|
+
stats_table.add_row("Transitions:", str(transition_count))
|
|
125
|
+
|
|
126
|
+
console.print(Panel(
|
|
127
|
+
stats_table,
|
|
128
|
+
title="[bold]Graph Statistics[/bold]",
|
|
129
|
+
style="cyan"
|
|
130
|
+
))
|
|
131
|
+
|
|
132
|
+
# Collect all valid state names
|
|
133
|
+
valid_states: Set[str] = set(graph.nodes())
|
|
134
|
+
|
|
135
|
+
# Collect all state names referenced in transitions
|
|
136
|
+
referenced_states: Set[str] = set()
|
|
137
|
+
for u, v in graph.edges():
|
|
138
|
+
referenced_states.add(u)
|
|
139
|
+
referenced_states.add(v)
|
|
140
|
+
|
|
141
|
+
console.print()
|
|
142
|
+
console.print(Panel("[bold]Graph Checks[/bold]", style="cyan"))
|
|
143
|
+
console.print()
|
|
144
|
+
|
|
145
|
+
has_warnings = False
|
|
146
|
+
has_info = False
|
|
147
|
+
|
|
148
|
+
# Check for invalid state names in transitions (from captured errors)
|
|
149
|
+
if error_handler.errors:
|
|
150
|
+
has_warnings = True
|
|
151
|
+
console.print(f"[yellow]⚠️ WARNING: Found {len(error_handler.errors)} transition(s) with invalid state names:[/yellow]")
|
|
152
|
+
for error in error_handler.errors:
|
|
153
|
+
# Extract the relevant info from the error message
|
|
154
|
+
console.print(f" [yellow]• {error.replace('Error: ', '')}[/yellow]")
|
|
155
|
+
console.print()
|
|
156
|
+
|
|
157
|
+
# Check for orphaned states (no incoming AND no outgoing transitions)
|
|
158
|
+
orphaned = [
|
|
159
|
+
node for node in graph.nodes()
|
|
160
|
+
if graph.in_degree(node) == 0 and graph.out_degree(node) == 0
|
|
161
|
+
]
|
|
162
|
+
if orphaned:
|
|
163
|
+
has_warnings = True
|
|
164
|
+
console.print(f"[yellow]⚠️ WARNING: Found {len(orphaned)} orphaned state(s) (no incoming or outgoing transitions):[/yellow]")
|
|
165
|
+
for state in sorted(orphaned):
|
|
166
|
+
console.print(f" [yellow]• {state}[/yellow]")
|
|
167
|
+
console.print()
|
|
168
|
+
|
|
169
|
+
# Info: States with no incoming transitions
|
|
170
|
+
no_incoming = [node for node in graph.nodes() if graph.in_degree(node) == 0 and graph.out_degree(node) > 0]
|
|
171
|
+
if no_incoming:
|
|
172
|
+
has_info = True
|
|
173
|
+
console.print(f"[blue]ℹ️ INFO: Found {len(no_incoming)} state(s) with no incoming transitions (entry points):[/blue]")
|
|
174
|
+
for state in sorted(no_incoming):
|
|
175
|
+
console.print(f" [blue]• {state}[/blue]")
|
|
176
|
+
console.print()
|
|
177
|
+
|
|
178
|
+
# Info: States with no outgoing transitions
|
|
179
|
+
no_outgoing = [node for node in graph.nodes() if graph.out_degree(node) == 0 and graph.in_degree(node) > 0]
|
|
180
|
+
if no_outgoing:
|
|
181
|
+
has_info = True
|
|
182
|
+
console.print(f"[blue]ℹ️ INFO: Found {len(no_outgoing)} state(s) with no outgoing transitions (terminal states):[/blue]")
|
|
183
|
+
for state in sorted(no_outgoing):
|
|
184
|
+
console.print(f" [blue]• {state}[/blue]")
|
|
185
|
+
console.print()
|
|
186
|
+
|
|
187
|
+
# All good message
|
|
188
|
+
if not has_warnings:
|
|
189
|
+
console.print("[green]✅ No issues detected.[/green]")
|
|
190
|
+
console.print()
|
|
191
|
+
|
|
192
|
+
except Exception as e:
|
|
193
|
+
console.print(f"\n[red]Error checking graph: {e}[/red]")
|
|
194
|
+
import traceback
|
|
195
|
+
console.print(f"[dim]{traceback.format_exc()}[/dim]")
|
|
196
|
+
sys.exit(1)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def main():
|
|
200
|
+
"""Entry point for standalone script execution"""
|
|
201
|
+
import argparse
|
|
202
|
+
|
|
203
|
+
parser = argparse.ArgumentParser(description="Check ScreenPilot state machine graph")
|
|
204
|
+
parser.add_argument(
|
|
205
|
+
"--module-path",
|
|
206
|
+
required=False,
|
|
207
|
+
default=None,
|
|
208
|
+
help="Path to the Python file containing the state machine (defaults to src/main.py)"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
args = parser.parse_args()
|
|
212
|
+
check_graph(args.module_path)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
if __name__ == "__main__":
|
|
216
|
+
main()
|
|
@@ -318,9 +318,9 @@ def main():
|
|
|
318
318
|
run_id="test-session-run",
|
|
319
319
|
)
|
|
320
320
|
|
|
321
|
-
#
|
|
322
|
-
|
|
323
|
-
|
|
321
|
+
# The @gui_automation decorator will establish the connection
|
|
322
|
+
# and the function itself will print the connection status
|
|
323
|
+
start_interactive_session(payload)
|
|
324
324
|
|
|
325
325
|
|
|
326
326
|
if __name__ == "__main__":
|
|
@@ -145,6 +145,25 @@ def create_init_py(target_dir: Path) -> None:
|
|
|
145
145
|
console.print(f"[green]+[/green] Created {init_path}")
|
|
146
146
|
|
|
147
147
|
|
|
148
|
+
def create_vscode_launch_config() -> None:
|
|
149
|
+
"""Create .vscode/launch.json for debugging."""
|
|
150
|
+
vscode_dir = Path(".vscode")
|
|
151
|
+
vscode_dir.mkdir(exist_ok=True)
|
|
152
|
+
|
|
153
|
+
launch_path = vscode_dir / "launch.json"
|
|
154
|
+
|
|
155
|
+
if launch_path.exists():
|
|
156
|
+
console.print(f"[yellow]![/yellow] {launch_path} already exists, skipping...")
|
|
157
|
+
return
|
|
158
|
+
|
|
159
|
+
content = read_template("launch.json.template")
|
|
160
|
+
|
|
161
|
+
with open(launch_path, "w", encoding='utf-8') as f:
|
|
162
|
+
f.write(content)
|
|
163
|
+
|
|
164
|
+
console.print(f"[green]+[/green] Created {launch_path}")
|
|
165
|
+
|
|
166
|
+
|
|
148
167
|
def create_gui_structure(target_dir: Path) -> None:
|
|
149
168
|
"""Create GUI automation folder structure with template files.
|
|
150
169
|
|
|
@@ -241,6 +260,9 @@ def init_project(
|
|
|
241
260
|
# Create __init__.py
|
|
242
261
|
create_init_py(target_dir)
|
|
243
262
|
|
|
263
|
+
# Create VS Code launch configuration
|
|
264
|
+
create_vscode_launch_config()
|
|
265
|
+
|
|
244
266
|
# Create GUI automation structure if requested
|
|
245
267
|
if with_gui:
|
|
246
268
|
create_gui_structure(target_dir)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.2.0",
|
|
3
|
+
"configurations": [
|
|
4
|
+
{
|
|
5
|
+
"name": "Clerk: Debug Code Run",
|
|
6
|
+
"type": "debugpy",
|
|
7
|
+
"request": "attach",
|
|
8
|
+
"connect": {
|
|
9
|
+
"host": "localhost",
|
|
10
|
+
"port": 5678
|
|
11
|
+
},
|
|
12
|
+
"pathMappings": [
|
|
13
|
+
{
|
|
14
|
+
"localRoot": "${workspaceFolder}/src",
|
|
15
|
+
"remoteRoot": "${workspaceFolder}/src"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -19,7 +19,7 @@ def main(payload: ClerkCodePayload):
|
|
|
19
19
|
data = StructuredData.model_validate(payload.structured_data)
|
|
20
20
|
logger.info("Custom code started")
|
|
21
21
|
|
|
22
|
-
def goal_function(current_state: str,
|
|
22
|
+
def goal_function(current_state: str, data: StructuredData) -> None:
|
|
23
23
|
"""
|
|
24
24
|
Goal function for the state machine.
|
|
25
25
|
Args:
|
|
@@ -31,7 +31,7 @@ def main(payload: ClerkCodePayload):
|
|
|
31
31
|
if True: # Use an actual condition based on state and/or process variables to complete the automation
|
|
32
32
|
raise SuccessfulCompletion()
|
|
33
33
|
|
|
34
|
-
ai_recovery_instructions = "
|
|
34
|
+
ai_recovery_instructions = "\n - ".join(
|
|
35
35
|
[
|
|
36
36
|
"Instructions on how to deal with possible issues using Clerk UI Actions.",
|
|
37
37
|
]
|
|
@@ -39,7 +39,7 @@ def main(payload: ClerkCodePayload):
|
|
|
39
39
|
|
|
40
40
|
ScreenPilot.configure(ai_recovery_instructions=ai_recovery_instructions)
|
|
41
41
|
exit_reason = ScreenPilot.run(
|
|
42
|
-
goal_function,
|
|
42
|
+
goal_function, data=data, doc_id=payload.document.id
|
|
43
43
|
)
|
|
44
44
|
|
|
45
45
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "2.0.0",
|
|
3
|
+
"tasks": [
|
|
4
|
+
{
|
|
5
|
+
"label": "Start Clerk Code Run Debug Server",
|
|
6
|
+
"type": "shell",
|
|
7
|
+
"command": "clerk",
|
|
8
|
+
"args": ["code", "run"],
|
|
9
|
+
"isBackground": true,
|
|
10
|
+
"problemMatcher": {
|
|
11
|
+
"pattern": {
|
|
12
|
+
"regexp": "^$",
|
|
13
|
+
"file": 1,
|
|
14
|
+
"location": 2,
|
|
15
|
+
"message": 3
|
|
16
|
+
},
|
|
17
|
+
"background": {
|
|
18
|
+
"activeOnStart": true,
|
|
19
|
+
"beginsPattern": "Starting debug server",
|
|
20
|
+
"endsPattern": "Waiting for debugger to attach"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Test payload for local testing."""
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from clerk.decorator.models import ClerkCodePayload, Document, File
|
|
4
|
+
from src.schema import *
|
|
5
|
+
|
|
6
|
+
# Define your test payload here
|
|
7
|
+
#
|
|
8
|
+
# NOTE: Files can be local! The mock Clerk client will read from local paths.
|
|
9
|
+
# Put your test files in test/payloads/ and reference them like:
|
|
10
|
+
# File(name="invoice.pdf", url="test/payloads/invoice.pdf")
|
|
11
|
+
#
|
|
12
|
+
# When your code calls client.get_files_document(), it will return ParsedFile
|
|
13
|
+
# objects with the actual content from these local files.
|
|
14
|
+
|
|
15
|
+
payload = ClerkCodePayload(
|
|
16
|
+
document=Document(
|
|
17
|
+
id="test-doc-123",
|
|
18
|
+
message_subject="Test Document",
|
|
19
|
+
message_content="This is a test document for local testing",
|
|
20
|
+
files=[
|
|
21
|
+
File(
|
|
22
|
+
name="example.pdf",
|
|
23
|
+
url="test/payloads/example.pdf" # Use local file path
|
|
24
|
+
)
|
|
25
|
+
],
|
|
26
|
+
upload_date=datetime(2025, 1, 1, 0, 0, 0)
|
|
27
|
+
),
|
|
28
|
+
structured_data=StructuredData(
|
|
29
|
+
{structured_data_fields}
|
|
30
|
+
).model_dump(),
|
|
31
|
+
run_id="test-run-001"
|
|
32
|
+
)
|
|
@@ -58,7 +58,7 @@ async def _perform_action_ws(payload: Dict[str, Any]) -> PerformActionResponse:
|
|
|
58
58
|
try:
|
|
59
59
|
ack = await asyncio.wait_for(global_ws.recv(), 10)
|
|
60
60
|
if ack == "OK":
|
|
61
|
-
action_info = await asyncio.wait_for(global_ws.recv(),
|
|
61
|
+
action_info = await asyncio.wait_for(global_ws.recv(), 60)
|
|
62
62
|
return PerformActionResponse(**json.loads(action_info))
|
|
63
63
|
else:
|
|
64
64
|
raise RuntimeError("Received ACK != OK")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clerk-sdk
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.4
|
|
4
4
|
Summary: Library for interacting with Clerk
|
|
5
5
|
Project-URL: Homepage, https://github.com/F-ONE-Group/clerk_pypi
|
|
6
6
|
Author-email: F-One <contact@f-one.group>
|
|
@@ -10,6 +10,7 @@ Classifier: Operating System :: OS Independent
|
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
11
|
Requires-Python: >=3.11
|
|
12
12
|
Requires-Dist: backoff<3.0.0,>=2.0.0
|
|
13
|
+
Requires-Dist: debugpy<2.0.0,>=1.8.0
|
|
13
14
|
Requires-Dist: pydantic<3.0.0,>=2.0.0
|
|
14
15
|
Requires-Dist: python-dotenv>=1.0.0
|
|
15
16
|
Requires-Dist: requests<3.0.0,>=2.32.3
|
|
@@ -1,19 +1,24 @@
|
|
|
1
|
-
clerk/__init__.py,sha256=
|
|
1
|
+
clerk/__init__.py,sha256=sQnCEW-ctm8zuDSgan3OWyjXaHvo3pZCgzRADiavzbI,50
|
|
2
2
|
clerk/base.py,sha256=lbFTdpdDfsmYIQUFH93S1aw0-L6GNJwAcubW1tdMFX4,3967
|
|
3
3
|
clerk/client.py,sha256=JGhDUb4m8R7ZTHLxFsAwAw0vNA1kxewg5-GC6qOFYDk,5244
|
|
4
4
|
clerk/decorator/__init__.py,sha256=yGGcS17VsZ7cZ-hVGCm3I3vGDJMiJIAqmDGzriIi0DI,65
|
|
5
5
|
clerk/decorator/models.py,sha256=nFMdVSG3nJ4hrEXs9YbI_GgjHbVjhSWZokOCzUh-lqQ,521
|
|
6
6
|
clerk/decorator/task_decorator.py,sha256=H8caRvNvvl-IRwyREP66gBGVM-SpQJ1W7oAFImO-6Jw,3769
|
|
7
7
|
clerk/development/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
clerk/development/cli.py,sha256
|
|
9
|
-
clerk/development/
|
|
10
|
-
clerk/development/
|
|
8
|
+
clerk/development/cli.py,sha256=hfe0FECLBsxUhio8mJvpkiu8d1lWNtXz3sW1c-vbcHk,5187
|
|
9
|
+
clerk/development/code_runner.py,sha256=GNMAO_Lb23jI6TnEWBoevzuha8OW8icS30teR3Qe5xs,14350
|
|
10
|
+
clerk/development/init_project.py,sha256=Rqw_CVyPzAxNj0LVQSJfDC-Xt6ldvtjiM_QNM2ExB5Q,11304
|
|
11
|
+
clerk/development/gui/graph_checker.py,sha256=3PaKplOt6UN-zeYtGmwMF42CjVQfQEV1xo7U-uiKEfs,8176
|
|
12
|
+
clerk/development/gui/test_session.py,sha256=TRhkachA8jEYGOqA5Xda0Oddo8AcVFe7urkjURGtqj8,10527
|
|
11
13
|
clerk/development/schema/fetch_schema.py,sha256=jIGO2ZFvgVnFz5_pw1C2Nf48OR1skZEf7fTyOLJAg5A,12046
|
|
12
14
|
clerk/development/templates/exceptions.py.template,sha256=BN9MhFhH435WX3e6HTd7bmakOD6Kh24bDPDMHBrArvQ,446
|
|
15
|
+
clerk/development/templates/launch.json.template,sha256=BoaXaQwuwekyZgNydifnB5Xh_VpQzSygOAXNBt8-1Xk,491
|
|
13
16
|
clerk/development/templates/main_basic.py.template,sha256=9Cs5BC8FK2uFbxYEJfyHfpzbSz9tq2Q47XdUncbL0rM,550
|
|
14
|
-
clerk/development/templates/main_gui.py.template,sha256=
|
|
17
|
+
clerk/development/templates/main_gui.py.template,sha256=sg0BcWBRFykXzR8AmxNZo5CTWZzVcwJtEqFFSKyEJR4,1756
|
|
15
18
|
clerk/development/templates/rollbacks.py.template,sha256=VeCVJhEwqol9LwRL5OfxIQUSOadbhGYYWrqjuqxO1bM,486
|
|
16
19
|
clerk/development/templates/states.py.template,sha256=d0OjZTdP8DNMt4YE0PIBPQ8jSpgll4u31s6n9aSJ1Mw,404
|
|
20
|
+
clerk/development/templates/tasks.json.template,sha256=mrx-YZEokdv8RsqxxFeW-hi8K8w5VrqLVxkgoJ3djPk,711
|
|
21
|
+
clerk/development/templates/test_payload.py.template,sha256=7X5hDITUWNngrIAREn6TPt13gGReHlfqyXjurv7kNbI,1062
|
|
17
22
|
clerk/development/templates/transitions.py.template,sha256=PtkBbRxbEzqTqRa7zNDxrk8I1UhrithhBSKaO9r65uA,728
|
|
18
23
|
clerk/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
24
|
clerk/exceptions/exceptions.py,sha256=gSCma06b6W6c0NrA2rhzd5YjFhZGa6caShX07lo-_3E,1291
|
|
@@ -25,7 +30,7 @@ clerk/gui_automation/action_model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeR
|
|
|
25
30
|
clerk/gui_automation/action_model/model.py,sha256=yzaCyEMOH3YMkPBf6IwUMuu69-xyf78HzmthiewgWQY,3811
|
|
26
31
|
clerk/gui_automation/action_model/utils.py,sha256=xzFxgN-bTK6HKGS7J-esQZ-ePj_yG72T-2ZVhcWvKjw,798
|
|
27
32
|
clerk/gui_automation/client_actor/__init__.py,sha256=SVuL6-oo1Xc0oJkjMKrO6mJwpPGjrCLKhDV6r2Abtf8,66
|
|
28
|
-
clerk/gui_automation/client_actor/client_actor.py,sha256=
|
|
33
|
+
clerk/gui_automation/client_actor/client_actor.py,sha256=05gju2A7kvklO1vjc2v6UJ_jKLFUXDQuH7952Qq6loE,5475
|
|
29
34
|
clerk/gui_automation/client_actor/exception.py,sha256=zdnImHZ88yf52Xq3aMHivEU3aJg-r2c-r8x8XZnI3ic,407
|
|
30
35
|
clerk/gui_automation/client_actor/model.py,sha256=wVpFCi1w2kh4kAV8oNx489vf_SLUQnqhc02rFD5NIJA,6335
|
|
31
36
|
clerk/gui_automation/decorators/__init__.py,sha256=OCgXStEumscgT-RyVy5OKS7ml1w9y-lEnjCVnxuRnQs,43
|
|
@@ -58,8 +63,8 @@ clerk/models/ui_operator.py,sha256=mKTJUFZgv7PeEt5oys28HVZxHOJsofmRQOcRpqj0dbU,2
|
|
|
58
63
|
clerk/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
64
|
clerk/utils/logger.py,sha256=NrMIlJfVmRjjRw_N_Jngkl0qqv7btXUbg5wxcRmFEH4,3800
|
|
60
65
|
clerk/utils/save_artifact.py,sha256=94aYkYNVGcSUaSWZmdjiY6Oc-3yCKb2XWCZ56IAXQqk,1158
|
|
61
|
-
clerk_sdk-0.5.
|
|
62
|
-
clerk_sdk-0.5.
|
|
63
|
-
clerk_sdk-0.5.
|
|
64
|
-
clerk_sdk-0.5.
|
|
65
|
-
clerk_sdk-0.5.
|
|
66
|
+
clerk_sdk-0.5.4.dist-info/METADATA,sha256=bn-CQWNQvCxpMBSl2mrPC-10sVtwvmUrt0ekLQ0FcfU,9635
|
|
67
|
+
clerk_sdk-0.5.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
68
|
+
clerk_sdk-0.5.4.dist-info/entry_points.txt,sha256=VoUmW07sRRSioms5pqQ4A6CYxNEyhGA93GtyBlB_wGw,53
|
|
69
|
+
clerk_sdk-0.5.4.dist-info/licenses/LICENSE,sha256=GTVQl3vH6ht70wJXKC0yMT8CmXKHxv_YyO_utAgm7EA,1065
|
|
70
|
+
clerk_sdk-0.5.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|