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.
- cli/__init__.py +0 -0
- cli/_version.py +1 -0
- cli/dispatch.py +266 -0
- cli/doctor.py +487 -0
- cli/modes/__init__.py +0 -0
- cli/modes/action.py +262 -0
- cli/modes/default.py +248 -0
- cli/modes/demo.py +162 -0
- cli/modes/dry_run.py +237 -0
- cli/modes/init.py +133 -0
- cli/modes/plan.py +148 -0
- cli/modes/workflow.py +354 -0
- cli/parser.py +305 -0
- cli/reporter.py +207 -0
- cli/session.py +146 -0
- cli/shared.py +427 -0
- cli/shorthand.py +90 -0
- cli/tool_protocol_handlers.py +446 -0
- common/__init__.py +0 -0
- common/adapters/__init__.py +21 -0
- common/adapters/android_adapter.py +273 -0
- common/adapters/base_adapter.py +24 -0
- common/adapters/ios_adapter.py +278 -0
- common/adapters/web_adapter.py +271 -0
- common/ai.py +277 -0
- common/ai_autonomous.py +273 -0
- common/ai_heal.py +222 -0
- common/cache/__init__.py +15 -0
- common/cache/cache_hash.py +57 -0
- common/cache/cache_manager.py +300 -0
- common/cache/cache_stats.py +133 -0
- common/cache/cache_storage.py +79 -0
- common/cache/embedding_loader.py +150 -0
- common/capabilities.py +121 -0
- common/case_memory.py +327 -0
- common/error_codes.py +61 -0
- common/exceptions.py +18 -0
- common/executor.py +1504 -0
- common/failure_diagnosis.py +138 -0
- common/history_manager.py +75 -0
- common/logs.py +168 -0
- common/mcp_server.py +467 -0
- common/preflight.py +496 -0
- common/progress.py +37 -0
- common/run_reporter.py +415 -0
- common/run_resume.py +149 -0
- common/runtime_modes.py +35 -0
- common/tool_protocol.py +196 -0
- common/visual_fallback.py +71 -0
- common/workflow_schema.py +150 -0
- config/__init__.py +0 -0
- config/config.py +167 -0
- config/env_loader.py +76 -0
- screenforge-0.4.0.dist-info/METADATA +43 -0
- screenforge-0.4.0.dist-info/RECORD +64 -0
- screenforge-0.4.0.dist-info/WHEEL +5 -0
- screenforge-0.4.0.dist-info/entry_points.txt +2 -0
- screenforge-0.4.0.dist-info/licenses/LICENSE +21 -0
- screenforge-0.4.0.dist-info/top_level.txt +4 -0
- utils/__init__.py +0 -0
- utils/screenshot_annotator.py +60 -0
- utils/utils_ios.py +195 -0
- utils/utils_web.py +304 -0
- 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)
|