screenforge 0.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.
Files changed (64) hide show
  1. cli/__init__.py +0 -0
  2. cli/_version.py +1 -0
  3. cli/dispatch.py +266 -0
  4. cli/doctor.py +487 -0
  5. cli/modes/__init__.py +0 -0
  6. cli/modes/action.py +262 -0
  7. cli/modes/default.py +248 -0
  8. cli/modes/demo.py +162 -0
  9. cli/modes/dry_run.py +237 -0
  10. cli/modes/init.py +133 -0
  11. cli/modes/plan.py +148 -0
  12. cli/modes/workflow.py +354 -0
  13. cli/parser.py +305 -0
  14. cli/reporter.py +207 -0
  15. cli/session.py +146 -0
  16. cli/shared.py +427 -0
  17. cli/shorthand.py +90 -0
  18. cli/tool_protocol_handlers.py +446 -0
  19. common/__init__.py +0 -0
  20. common/adapters/__init__.py +21 -0
  21. common/adapters/android_adapter.py +273 -0
  22. common/adapters/base_adapter.py +24 -0
  23. common/adapters/ios_adapter.py +278 -0
  24. common/adapters/web_adapter.py +271 -0
  25. common/ai.py +277 -0
  26. common/ai_autonomous.py +273 -0
  27. common/ai_heal.py +222 -0
  28. common/cache/__init__.py +15 -0
  29. common/cache/cache_hash.py +57 -0
  30. common/cache/cache_manager.py +300 -0
  31. common/cache/cache_stats.py +133 -0
  32. common/cache/cache_storage.py +79 -0
  33. common/cache/embedding_loader.py +150 -0
  34. common/capabilities.py +121 -0
  35. common/case_memory.py +327 -0
  36. common/error_codes.py +61 -0
  37. common/exceptions.py +18 -0
  38. common/executor.py +1504 -0
  39. common/failure_diagnosis.py +138 -0
  40. common/history_manager.py +75 -0
  41. common/logs.py +168 -0
  42. common/mcp_server.py +467 -0
  43. common/preflight.py +496 -0
  44. common/progress.py +37 -0
  45. common/run_reporter.py +415 -0
  46. common/run_resume.py +149 -0
  47. common/runtime_modes.py +35 -0
  48. common/tool_protocol.py +196 -0
  49. common/visual_fallback.py +71 -0
  50. common/workflow_schema.py +150 -0
  51. config/__init__.py +0 -0
  52. config/config.py +167 -0
  53. config/env_loader.py +76 -0
  54. screenforge-0.4.0.dist-info/METADATA +43 -0
  55. screenforge-0.4.0.dist-info/RECORD +64 -0
  56. screenforge-0.4.0.dist-info/WHEEL +5 -0
  57. screenforge-0.4.0.dist-info/entry_points.txt +2 -0
  58. screenforge-0.4.0.dist-info/licenses/LICENSE +21 -0
  59. screenforge-0.4.0.dist-info/top_level.txt +4 -0
  60. utils/__init__.py +0 -0
  61. utils/screenshot_annotator.py +60 -0
  62. utils/utils_ios.py +195 -0
  63. utils/utils_web.py +304 -0
  64. utils/utils_xml.py +218 -0
cli/__init__.py ADDED
File without changes
cli/_version.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.4.0"
cli/dispatch.py ADDED
@@ -0,0 +1,266 @@
1
+ """Execution dispatcher and CLI entry point."""
2
+
3
+ import sys
4
+
5
+ from cli.doctor import run_capabilities_mode, run_doctor_mode
6
+ from cli.modes.action import run_action_default_mode
7
+ from cli.modes.default import run_default_mode
8
+ from cli.modes.dry_run import run_action_dry_run_mode, run_dry_run_mode
9
+ from cli.modes.plan import run_action_plan_only_mode, run_plan_only_mode
10
+ from cli.modes.workflow import (
11
+ run_workflow_default_mode,
12
+ run_workflow_dry_run_mode,
13
+ run_workflow_plan_only_mode,
14
+ )
15
+ from cli.parser import build_parser, validate_cli_args
16
+ from cli.reporter import _load_context_content, _resolve_output_script_path
17
+ from cli.shared import _SharedAdapterManager, config, log
18
+ from cli.tool_protocol_handlers import (
19
+ _requires_model_runtime,
20
+ run_mcp_server_mode,
21
+ run_tool_request_mode,
22
+ run_tool_stdin_mode,
23
+ )
24
+ from common.run_resume import RunContextLoadError
25
+ from common.runtime_modes import (
26
+ MODE_DOCTOR,
27
+ MODE_DRY_RUN,
28
+ MODE_PLAN_ONLY,
29
+ resolve_execution_mode,
30
+ )
31
+
32
+
33
+ def _dispatch_execution(
34
+ args,
35
+ execution_mode: str,
36
+ output_script_path: str,
37
+ context_content: str,
38
+ resume_context: dict,
39
+ shared_adapter_manager: _SharedAdapterManager | None = None,
40
+ ) -> int:
41
+ if execution_mode == MODE_DOCTOR:
42
+ return run_doctor_mode(args, output_script_path)
43
+ if args.workflow and execution_mode == MODE_PLAN_ONLY:
44
+ return run_workflow_plan_only_mode(
45
+ args,
46
+ output_script_path,
47
+ resume_context,
48
+ )
49
+ if args.workflow and execution_mode == MODE_DRY_RUN:
50
+ return run_workflow_dry_run_mode(
51
+ args,
52
+ output_script_path,
53
+ resume_context,
54
+ )
55
+ if args.workflow:
56
+ return run_workflow_default_mode(
57
+ args,
58
+ output_script_path,
59
+ resume_context,
60
+ )
61
+ if args.action and execution_mode == MODE_PLAN_ONLY:
62
+ return run_action_plan_only_mode(
63
+ args,
64
+ output_script_path,
65
+ resume_context,
66
+ )
67
+ if args.action and execution_mode == MODE_DRY_RUN:
68
+ return run_action_dry_run_mode(
69
+ args,
70
+ output_script_path,
71
+ resume_context,
72
+ )
73
+ if args.action:
74
+ return run_action_default_mode(
75
+ args,
76
+ output_script_path,
77
+ resume_context,
78
+ shared_adapter_manager=shared_adapter_manager,
79
+ )
80
+ if execution_mode == MODE_PLAN_ONLY:
81
+ return run_plan_only_mode(
82
+ args,
83
+ output_script_path,
84
+ context_content,
85
+ resume_context,
86
+ )
87
+ if execution_mode == MODE_DRY_RUN:
88
+ return run_dry_run_mode(
89
+ args,
90
+ output_script_path,
91
+ context_content,
92
+ resume_context,
93
+ )
94
+ return run_default_mode(
95
+ args,
96
+ output_script_path,
97
+ context_content,
98
+ resume_context,
99
+ )
100
+
101
+
102
+ def _run_session_end(args) -> int:
103
+ from cli.session import delete_session, load_session, stop_session_recording
104
+
105
+ session_id = args.session_end
106
+ session = load_session(session_id)
107
+ if not session:
108
+ log.error(f"❌ Session not found: {session_id}")
109
+ return 1
110
+
111
+ output_path = session["output_path"]
112
+
113
+ video_path = stop_session_recording(session_id)
114
+ if video_path:
115
+ log.info(f"🎬 [Session] Recording saved: {video_path}")
116
+
117
+ delete_session(session_id)
118
+ log.info(f"✅ [Session] Ended session '{session_id}'")
119
+ log.info(f"📄 [Session] Test script: {output_path}")
120
+ log.info(f"📊 [Session] Total steps: {session.get('steps', 0)}")
121
+ return 0
122
+
123
+
124
+ def main():
125
+ from cli.shorthand import preprocess_argv
126
+
127
+ processed_argv = preprocess_argv(sys.argv)
128
+
129
+ parser = build_parser()
130
+ args = parser.parse_args(processed_argv[1:])
131
+
132
+ if getattr(args, "session_end", ""):
133
+ sys.exit(_run_session_end(args))
134
+
135
+ if getattr(args, "web_stop", False):
136
+ from common.adapters.web_adapter import stop_persistent_browser
137
+
138
+ # Idempotent: stopping a browser, or finding none to stop, both succeed.
139
+ stop_persistent_browser()
140
+ sys.exit(0)
141
+
142
+ try:
143
+ validate_cli_args(args)
144
+ except ValueError as e:
145
+ log.error(f"[E010] Invalid CLI arguments: {e}. Fix: run 'screenforge --help' to see valid options")
146
+ sys.exit(2)
147
+
148
+ if getattr(args, "init", False):
149
+ from cli.modes.init import run_init_mode
150
+
151
+ sys.exit(run_init_mode())
152
+
153
+ if getattr(args, "demo", False):
154
+ from cli.modes.demo import run_demo_mode
155
+
156
+ sys.exit(run_demo_mode())
157
+
158
+ if getattr(args, "playground", False):
159
+ try:
160
+ from playground.app import run_server
161
+ except ImportError:
162
+ log.error("[E013] Playground requires extra dependencies. Fix: pip install screenforge[playground]")
163
+ sys.exit(1)
164
+ cdp_url = "http://127.0.0.1:9333"
165
+ log.info(f"Starting Playground on http://127.0.0.1:{args.playground_port}")
166
+ log.info(f"CDP screencast target: {cdp_url}")
167
+ run_server(port=args.playground_port, cdp_url=cdp_url)
168
+ sys.exit(0)
169
+
170
+ if args.tool_stdin:
171
+ from common.progress import set_tool_mode
172
+ set_tool_mode(True)
173
+ if sys.stdin.isatty():
174
+ import io
175
+ import json as _json
176
+ sys.stdin = io.StringIO(_json.dumps({"operation": "inspect_ui", "platform": args.platform}))
177
+ sys.exit(run_tool_stdin_mode(args))
178
+
179
+ if args.mcp_server:
180
+ from common.progress import set_tool_mode
181
+ set_tool_mode(True)
182
+ sys.exit(run_mcp_server_mode(args))
183
+
184
+ if args.tool_request:
185
+ from common.progress import set_tool_mode
186
+ set_tool_mode(True)
187
+ sys.exit(run_tool_request_mode(args))
188
+
189
+ if args.capabilities:
190
+ sys.exit(run_capabilities_mode(args))
191
+
192
+ execution_mode = resolve_execution_mode(
193
+ doctor=args.doctor,
194
+ plan_only=args.plan_only,
195
+ dry_run=args.dry_run,
196
+ )
197
+
198
+ session_id = getattr(args, "session_id", "")
199
+ shared_adapter_mgr = None
200
+ if session_id and args.action:
201
+ from cli.session import (
202
+ create_session,
203
+ load_session,
204
+ resolve_session_output_path,
205
+ update_session,
206
+ )
207
+
208
+ session = load_session(session_id)
209
+ if session:
210
+ output_script_path = session["output_path"]
211
+ shared_adapter_mgr = _SharedAdapterManager()
212
+ else:
213
+ from cli.session import start_session_recording
214
+
215
+ output_script_path = resolve_session_output_path(session_id, args.platform)
216
+ session = create_session(session_id, args.platform, output_script_path)
217
+ shared_adapter_mgr = _SharedAdapterManager()
218
+ start_session_recording(session_id, args.platform)
219
+ else:
220
+ output_script_path = _resolve_output_script_path(args)
221
+
222
+ log.info("=" * 60)
223
+ log.info("Starting ScreenForge UI automation engine")
224
+ target_label = (
225
+ args.goal
226
+ or getattr(args, "action_name", "")
227
+ or getattr(args, "action", "")
228
+ or args.workflow
229
+ or "doctor / no-goal mode"
230
+ )
231
+ log.info(f"Target: {target_label}")
232
+ log.info(f"Circuit breaker: max {args.max_retries} retries per step")
233
+ log.info(
234
+ f"Platform: {args.platform} | Vision: {'on' if args.vision else 'off'}"
235
+ )
236
+ log.info(f"Mode: {execution_mode}")
237
+ log.info(f"Output: {output_script_path}")
238
+ log.info("=" * 60)
239
+
240
+ try:
241
+ context_content, resume_context = _load_context_content(args)
242
+ except RunContextLoadError as e:
243
+ log.error(f"[E011] Failed to restore run context: {e}. Fix: check that report/runs/<run_id>/ exists and contains summary.json")
244
+ sys.exit(2)
245
+
246
+ if _requires_model_runtime(args, execution_mode) and not config.validate_config():
247
+ log.error("[E012] Configuration validation failed. See errors above for details and fix instructions")
248
+ sys.exit(1)
249
+
250
+ exit_code = _dispatch_execution(
251
+ args,
252
+ execution_mode,
253
+ output_script_path,
254
+ context_content,
255
+ resume_context,
256
+ shared_adapter_manager=shared_adapter_mgr,
257
+ )
258
+
259
+ if session_id and args.action:
260
+ from cli.session import load_session, update_session
261
+
262
+ session = load_session(session_id)
263
+ if session and exit_code == 0:
264
+ update_session(session_id, steps=session.get("steps", 0) + 1)
265
+
266
+ sys.exit(exit_code)