flowpilot-mobile 1.1.8__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.
Files changed (53) hide show
  1. flowpilot_mobile-1.1.8/PKG-INFO +153 -0
  2. flowpilot_mobile-1.1.8/README.md +127 -0
  3. flowpilot_mobile-1.1.8/flowpilot/__init__.py +7 -0
  4. flowpilot_mobile-1.1.8/flowpilot/api/__init__.py +0 -0
  5. flowpilot_mobile-1.1.8/flowpilot/api/mobile.py +284 -0
  6. flowpilot_mobile-1.1.8/flowpilot/cli/__init__.py +0 -0
  7. flowpilot_mobile-1.1.8/flowpilot/cli/interactive.py +76 -0
  8. flowpilot_mobile-1.1.8/flowpilot/cli/main.py +89 -0
  9. flowpilot_mobile-1.1.8/flowpilot/commands/__init__.py +0 -0
  10. flowpilot_mobile-1.1.8/flowpilot/commands/core.py +135 -0
  11. flowpilot_mobile-1.1.8/flowpilot/config/__init__.py +0 -0
  12. flowpilot_mobile-1.1.8/flowpilot/config/models.py +52 -0
  13. flowpilot_mobile-1.1.8/flowpilot/devices/__init__.py +0 -0
  14. flowpilot_mobile-1.1.8/flowpilot/devices/manager.py +26 -0
  15. flowpilot_mobile-1.1.8/flowpilot/elements/__init__.py +0 -0
  16. flowpilot_mobile-1.1.8/flowpilot/exceptions/__init__.py +0 -0
  17. flowpilot_mobile-1.1.8/flowpilot/exceptions/core.py +15 -0
  18. flowpilot_mobile-1.1.8/flowpilot/fixtures/__init__.py +0 -0
  19. flowpilot_mobile-1.1.8/flowpilot/reporting/__init__.py +0 -0
  20. flowpilot_mobile-1.1.8/flowpilot/reporting/engine.py +48 -0
  21. flowpilot_mobile-1.1.8/flowpilot/runtime/__init__.py +0 -0
  22. flowpilot_mobile-1.1.8/flowpilot/runtime/adapter.py +38 -0
  23. flowpilot_mobile-1.1.8/flowpilot/runtime/adb_engine.py +339 -0
  24. flowpilot_mobile-1.1.8/flowpilot/runtime/executor.py +183 -0
  25. flowpilot_mobile-1.1.8/flowpilot/runtime/guard.py +214 -0
  26. flowpilot_mobile-1.1.8/flowpilot/runtime/queue.py +16 -0
  27. flowpilot_mobile-1.1.8/flowpilot/runtime/translator.py +34 -0
  28. flowpilot_mobile-1.1.8/flowpilot/smart/__init__.py +0 -0
  29. flowpilot_mobile-1.1.8/flowpilot/smart/engine.py +74 -0
  30. flowpilot_mobile-1.1.8/flowpilot/utils/__init__.py +0 -0
  31. flowpilot_mobile-1.1.8/flowpilot/utils/helpers.py +24 -0
  32. flowpilot_mobile-1.1.8/flowpilot/utils/setup.py +221 -0
  33. flowpilot_mobile-1.1.8/flowpilot_mobile.egg-info/PKG-INFO +153 -0
  34. flowpilot_mobile-1.1.8/flowpilot_mobile.egg-info/SOURCES.txt +51 -0
  35. flowpilot_mobile-1.1.8/flowpilot_mobile.egg-info/dependency_links.txt +1 -0
  36. flowpilot_mobile-1.1.8/flowpilot_mobile.egg-info/entry_points.txt +5 -0
  37. flowpilot_mobile-1.1.8/flowpilot_mobile.egg-info/requires.txt +8 -0
  38. flowpilot_mobile-1.1.8/flowpilot_mobile.egg-info/top_level.txt +2 -0
  39. flowpilot_mobile-1.1.8/pyproject.toml +47 -0
  40. flowpilot_mobile-1.1.8/pytest_flowpilot/__init__.py +0 -0
  41. flowpilot_mobile-1.1.8/pytest_flowpilot/plugin.py +28 -0
  42. flowpilot_mobile-1.1.8/setup.cfg +4 -0
  43. flowpilot_mobile-1.1.8/tests/test_chrome_automation.py +46 -0
  44. flowpilot_mobile-1.1.8/tests/test_chrome_login.py +45 -0
  45. flowpilot_mobile-1.1.8/tests/test_double_swipe.py +27 -0
  46. flowpilot_mobile-1.1.8/tests/test_gestures.py +31 -0
  47. flowpilot_mobile-1.1.8/tests/test_google_search_simple.py +31 -0
  48. flowpilot_mobile-1.1.8/tests/test_physical_device.py +34 -0
  49. flowpilot_mobile-1.1.8/tests/test_scroll_find.py +19 -0
  50. flowpilot_mobile-1.1.8/tests/test_settings_google.py +30 -0
  51. flowpilot_mobile-1.1.8/tests/test_text_discovery.py +31 -0
  52. flowpilot_mobile-1.1.8/tests/test_visible_phone.py +24 -0
  53. flowpilot_mobile-1.1.8/tests/test_widget_swipe.py +26 -0
@@ -0,0 +1,153 @@
1
+ Metadata-Version: 2.4
2
+ Name: flowpilot-mobile
3
+ Version: 1.1.8
4
+ Summary: Modern, Intelligent Mobile Automation Framework
5
+ Author-email: Niranjan Kumar <niranjan4@outlook.in>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/niranjangs4/flowpilot-mobile
8
+ Project-URL: Documentation, https://github.com/niranjangs4/flowpilot-mobile#readme
9
+ Keywords: mobile,automation,android,smart,testing,pytest
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Topic :: Software Development :: Testing
14
+ Classifier: Operating System :: Microsoft :: Windows
15
+ Classifier: Operating System :: MacOS :: MacOS X
16
+ Requires-Python: >=3.9
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: pytest>=8.0.0
19
+ Requires-Dist: pytest-xdist>=3.5.0
20
+ Requires-Dist: rich>=13.0.0
21
+ Requires-Dist: loguru>=0.7.0
22
+ Requires-Dist: pydantic>=2.0.0
23
+ Requires-Dist: pyyaml>=6.0.0
24
+ Requires-Dist: jinja2>=3.1.0
25
+ Requires-Dist: cryptography>=41.0.0
26
+
27
+ # FlowPilot Mobile
28
+
29
+ Modern, Python-first mobile automation framework.
30
+ Provides clean Playwright-style APIs for smart mobile automation with AI-assisted test generation.
31
+
32
+ ## 🚀 Quick Start
33
+
34
+ ### 1. Install via pip
35
+ ```bash
36
+ pip install flowpilot-mobile
37
+ ```
38
+
39
+ ### 2. Initialize your Project
40
+ Initialize a new project folder with all necessary configurations:
41
+ ```bash
42
+ flowpilot setup --project_folder my_tests
43
+ ```
44
+
45
+ ### 3. Configure Tool Paths
46
+ Open `my_tests/flowpilot.yaml` and specify your manual paths for ADB and Scrcpy if they are not in your system PATH:
47
+ ```yaml
48
+ tools:
49
+ adb_path: "C:\\path\\to\\adb.exe"
50
+ scrcpy_path: "C:\\path\\to\\scrcpy.exe"
51
+ ```
52
+
53
+ ---
54
+
55
+ ## 🌟 The FlowPilot "Smart" Advantage
56
+
57
+ FlowPilot includes an **Intelligent Layer** that handles the "messy" parts of mobile testing automatically:
58
+
59
+ - **Auto-Scroll Search**: Automatically swipes and retries if an element is off-screen.
60
+ - **Smart Locator Scoring**: Distinguishes between actual inputs and "distractor" elements (like Search Labs icons).
61
+ - **Nearby Label Matching**: Fills input fields based on the text labels next to them.
62
+ - **Self-Healing**: Uses fuzzy matching for minor UI text changes (e.g., "Log In" vs "Login").
63
+ - **Automatic Device Reset**: Automatically presses the **Home button** before and after every test to ensure a clean baseline.
64
+ - **Zero-Sleep Synchronization**: Built-in polling ensures elements are ready before interaction.
65
+
66
+ ---
67
+
68
+ ## 🤖 AI-Assisted Automation (TEST_GEN_PROMPT)
69
+
70
+ FlowPilot now supports **Instant Test Generation** via AI. Every project initialized with `flowpilot setup` includes a `TEST_GEN_PROMPT.md` file.
71
+
72
+ 1. Copy the content of `TEST_GEN_PROMPT.md`.
73
+ 2. Paste it into **Gemini** or **ChatGPT**.
74
+ 3. Provide your manual test steps or describe a screen recording.
75
+ 4. **Result:** The AI will generate a complete FlowPilot Python test script for you.
76
+
77
+ ---
78
+
79
+ ## 🖥️ Live Monitoring & Evidence
80
+
81
+ ### Live Desktop Mirroring
82
+ See your phone's screen in a window on your desktop (Mac/Windows) during execution.
83
+ - **Enable:** Set `mirror_screen: true` in your `flowpilot.yaml`.
84
+ - **Manual Path:** Configure `scrcpy_path` in `flowpilot.yaml` if needed.
85
+
86
+ ### Automated Evidence
87
+ - **Screen Recording (MP4)**: Every test is recorded with hardware encoding (zero performance drop). Includes professional 1s padding.
88
+ - **Retention Policy**: Automatically purges recordings older than **5 days** (configurable) to save disk space.
89
+ - **Smart Screenshots**: Captures specific UI states into `.flowpilot/screenshots/`.
90
+ - **Selective Cleanup**: Wipes temporary flows and recent screenshots before each run while **preserving** your video history.
91
+
92
+ ---
93
+
94
+ ## ⚙️ Configuration (`flowpilot.yaml`)
95
+ ```yaml
96
+ project_name: "My Custom App"
97
+
98
+ # Manual Tool Paths
99
+ tools:
100
+ adb_path: ""
101
+ scrcpy_path: ""
102
+
103
+ # Evidence & Storage
104
+ output_dir: ".flowpilot"
105
+ recordings_dir: ".flowpilot/recordings"
106
+ screenshots_dir: ".flowpilot/screenshots"
107
+
108
+ # Features
109
+ record_video: true
110
+ mirror_screen: true
111
+
112
+ # Smart Engine Settings
113
+ auto_scroll: true
114
+ timeout: 30
115
+ ```
116
+
117
+ ---
118
+
119
+ ## 📖 Public API Reference
120
+
121
+ | Category | Method | Description | Example |
122
+ | :--- | :--- | :--- | :--- |
123
+ | **Lifecycle** | `launch_app(pkg)` | Opens a specific app (defaults to Chrome). | `mobile.launch_app()` |
124
+ | | `home()` | Returns to the device Home Screen. | `mobile.home()` |
125
+ | | `back()` | Simulates the hardware Back button. | `mobile.back()` |
126
+ | | `navigate(url)` | Opens a specific URL in the browser. | `mobile.navigate("https://google.com")` |
127
+ | **Interactions** | `tap(target)` | **Smart Tap**: Auto-scrolls and waits for visibility. | `mobile.tap("Settings")` |
128
+ | | `fill(target, val)` | **Smart Fill**: Finds nearest input to a label. | `mobile.fill("Username", "admin")` |
129
+ | | `fill_focused(val)` | **Active Field**: Types into currently focused input. | `mobile.fill_focused("secret")` |
130
+ | | `press_key(key)` | Presses hardware keys (Enter, Home, Back). | `mobile.press_key("Enter")` |
131
+ | **Gestures** | `open_notifications()`| Pulls down the notification shade (Left). | `mobile.open_notifications()` |
132
+ | | `open_quick_settings()`| Pulls down full Quick Settings (Center). | `mobile.open_quick_settings()` |
133
+ | | `scroll_down()` | Performs a smart downward scroll. | `mobile.scroll_down()` |
134
+ | **Media** | `screenshot(name)` | Saves a screenshot to `.flowpilot/screenshots/`. | `mobile.screenshot("step_1")` |
135
+
136
+ ---
137
+
138
+ ## 🤖 CLI Commands
139
+
140
+ | Command | Description |
141
+ | :--- | :--- |
142
+ | `flowpilot setup` | Initializes a new project folder and generates configurations. |
143
+ | `flowpilot interactive`| Start a live step-by-step automation session. |
144
+ | `flowpilot run` | Executes tests with smart logs. |
145
+ | `flowpilot devices` | Lists all connected physical Android devices. |
146
+
147
+ ---
148
+
149
+ ## 🛡 Engineering Principles
150
+ 1. **Hide Complexity**: Zero YAML or runtime engine logic exposed.
151
+ 2. **Text-First**: Locators prioritize display text over brittle XPaths.
152
+ 3. **Hardware-Accelerated**: Native MP4 capture and Scrcpy mirroring with zero host lag.
153
+ 4. **Resilient Reset**: Automated pre/post Home screen logic for repeatability.
@@ -0,0 +1,127 @@
1
+ # FlowPilot Mobile
2
+
3
+ Modern, Python-first mobile automation framework.
4
+ Provides clean Playwright-style APIs for smart mobile automation with AI-assisted test generation.
5
+
6
+ ## 🚀 Quick Start
7
+
8
+ ### 1. Install via pip
9
+ ```bash
10
+ pip install flowpilot-mobile
11
+ ```
12
+
13
+ ### 2. Initialize your Project
14
+ Initialize a new project folder with all necessary configurations:
15
+ ```bash
16
+ flowpilot setup --project_folder my_tests
17
+ ```
18
+
19
+ ### 3. Configure Tool Paths
20
+ Open `my_tests/flowpilot.yaml` and specify your manual paths for ADB and Scrcpy if they are not in your system PATH:
21
+ ```yaml
22
+ tools:
23
+ adb_path: "C:\\path\\to\\adb.exe"
24
+ scrcpy_path: "C:\\path\\to\\scrcpy.exe"
25
+ ```
26
+
27
+ ---
28
+
29
+ ## 🌟 The FlowPilot "Smart" Advantage
30
+
31
+ FlowPilot includes an **Intelligent Layer** that handles the "messy" parts of mobile testing automatically:
32
+
33
+ - **Auto-Scroll Search**: Automatically swipes and retries if an element is off-screen.
34
+ - **Smart Locator Scoring**: Distinguishes between actual inputs and "distractor" elements (like Search Labs icons).
35
+ - **Nearby Label Matching**: Fills input fields based on the text labels next to them.
36
+ - **Self-Healing**: Uses fuzzy matching for minor UI text changes (e.g., "Log In" vs "Login").
37
+ - **Automatic Device Reset**: Automatically presses the **Home button** before and after every test to ensure a clean baseline.
38
+ - **Zero-Sleep Synchronization**: Built-in polling ensures elements are ready before interaction.
39
+
40
+ ---
41
+
42
+ ## 🤖 AI-Assisted Automation (TEST_GEN_PROMPT)
43
+
44
+ FlowPilot now supports **Instant Test Generation** via AI. Every project initialized with `flowpilot setup` includes a `TEST_GEN_PROMPT.md` file.
45
+
46
+ 1. Copy the content of `TEST_GEN_PROMPT.md`.
47
+ 2. Paste it into **Gemini** or **ChatGPT**.
48
+ 3. Provide your manual test steps or describe a screen recording.
49
+ 4. **Result:** The AI will generate a complete FlowPilot Python test script for you.
50
+
51
+ ---
52
+
53
+ ## 🖥️ Live Monitoring & Evidence
54
+
55
+ ### Live Desktop Mirroring
56
+ See your phone's screen in a window on your desktop (Mac/Windows) during execution.
57
+ - **Enable:** Set `mirror_screen: true` in your `flowpilot.yaml`.
58
+ - **Manual Path:** Configure `scrcpy_path` in `flowpilot.yaml` if needed.
59
+
60
+ ### Automated Evidence
61
+ - **Screen Recording (MP4)**: Every test is recorded with hardware encoding (zero performance drop). Includes professional 1s padding.
62
+ - **Retention Policy**: Automatically purges recordings older than **5 days** (configurable) to save disk space.
63
+ - **Smart Screenshots**: Captures specific UI states into `.flowpilot/screenshots/`.
64
+ - **Selective Cleanup**: Wipes temporary flows and recent screenshots before each run while **preserving** your video history.
65
+
66
+ ---
67
+
68
+ ## ⚙️ Configuration (`flowpilot.yaml`)
69
+ ```yaml
70
+ project_name: "My Custom App"
71
+
72
+ # Manual Tool Paths
73
+ tools:
74
+ adb_path: ""
75
+ scrcpy_path: ""
76
+
77
+ # Evidence & Storage
78
+ output_dir: ".flowpilot"
79
+ recordings_dir: ".flowpilot/recordings"
80
+ screenshots_dir: ".flowpilot/screenshots"
81
+
82
+ # Features
83
+ record_video: true
84
+ mirror_screen: true
85
+
86
+ # Smart Engine Settings
87
+ auto_scroll: true
88
+ timeout: 30
89
+ ```
90
+
91
+ ---
92
+
93
+ ## 📖 Public API Reference
94
+
95
+ | Category | Method | Description | Example |
96
+ | :--- | :--- | :--- | :--- |
97
+ | **Lifecycle** | `launch_app(pkg)` | Opens a specific app (defaults to Chrome). | `mobile.launch_app()` |
98
+ | | `home()` | Returns to the device Home Screen. | `mobile.home()` |
99
+ | | `back()` | Simulates the hardware Back button. | `mobile.back()` |
100
+ | | `navigate(url)` | Opens a specific URL in the browser. | `mobile.navigate("https://google.com")` |
101
+ | **Interactions** | `tap(target)` | **Smart Tap**: Auto-scrolls and waits for visibility. | `mobile.tap("Settings")` |
102
+ | | `fill(target, val)` | **Smart Fill**: Finds nearest input to a label. | `mobile.fill("Username", "admin")` |
103
+ | | `fill_focused(val)` | **Active Field**: Types into currently focused input. | `mobile.fill_focused("secret")` |
104
+ | | `press_key(key)` | Presses hardware keys (Enter, Home, Back). | `mobile.press_key("Enter")` |
105
+ | **Gestures** | `open_notifications()`| Pulls down the notification shade (Left). | `mobile.open_notifications()` |
106
+ | | `open_quick_settings()`| Pulls down full Quick Settings (Center). | `mobile.open_quick_settings()` |
107
+ | | `scroll_down()` | Performs a smart downward scroll. | `mobile.scroll_down()` |
108
+ | **Media** | `screenshot(name)` | Saves a screenshot to `.flowpilot/screenshots/`. | `mobile.screenshot("step_1")` |
109
+
110
+ ---
111
+
112
+ ## 🤖 CLI Commands
113
+
114
+ | Command | Description |
115
+ | :--- | :--- |
116
+ | `flowpilot setup` | Initializes a new project folder and generates configurations. |
117
+ | `flowpilot interactive`| Start a live step-by-step automation session. |
118
+ | `flowpilot run` | Executes tests with smart logs. |
119
+ | `flowpilot devices` | Lists all connected physical Android devices. |
120
+
121
+ ---
122
+
123
+ ## 🛡 Engineering Principles
124
+ 1. **Hide Complexity**: Zero YAML or runtime engine logic exposed.
125
+ 2. **Text-First**: Locators prioritize display text over brittle XPaths.
126
+ 3. **Hardware-Accelerated**: Native MP4 capture and Scrcpy mirroring with zero host lag.
127
+ 4. **Resilient Reset**: Automated pre/post Home screen logic for repeatability.
@@ -0,0 +1,7 @@
1
+ from flowpilot.api.mobile import Mobile
2
+ from flowpilot.runtime.adb_engine import ADBRuntime
3
+ from flowpilot.runtime.executor import RuntimeExecutor
4
+ from flowpilot.utils.setup import SetupManager
5
+
6
+ __version__ = "1.1.9"
7
+ __all__ = ["Mobile", "ADBRuntime", "RuntimeExecutor", "SetupManager"]
File without changes
@@ -0,0 +1,284 @@
1
+ from loguru import logger
2
+ from flowpilot.commands.core import *
3
+ from flowpilot.runtime.queue import FlowContext
4
+ from flowpilot.runtime.executor import RuntimeExecutor
5
+
6
+ class SignatureAPI:
7
+ def __init__(self, context: FlowContext):
8
+ self._context = context
9
+
10
+ def draw(self):
11
+ # Internally use coordinate swipe actions
12
+ self._context.add_command(Command()) # Placeholder for DrawSignatureCommand
13
+ return self
14
+
15
+ class RelationalAPI:
16
+ def __init__(self, anchor: str, relation: str, context: FlowContext, mobile):
17
+ self.anchor = anchor
18
+ self.relation = relation
19
+ self.target_text = None
20
+ self.target_type = None
21
+ self._context = context
22
+ self._mobile = mobile
23
+
24
+ def text(self, text: str):
25
+ """Filters by specific text."""
26
+ self.target_text = text
27
+ return self
28
+
29
+ def type(self, type_name: str):
30
+ """Filters by class name (e.g., 'EditText', 'Button')."""
31
+ self.target_type = type_name
32
+ return self
33
+
34
+ def input(self):
35
+ """Shortcut to filter for EditText elements."""
36
+ self.target_type = "EditText"
37
+ return self
38
+
39
+ def tap(self):
40
+ self._context.add_command(RelationalCommand(
41
+ anchor=self.anchor,
42
+ relation=self.relation,
43
+ target_text=self.target_text,
44
+ target_type=self.target_type,
45
+ action="tap"
46
+ ))
47
+ return self._mobile
48
+
49
+ def fill(self, value: str):
50
+ self._context.add_command(RelationalCommand(
51
+ anchor=self.anchor,
52
+ relation=self.relation,
53
+ target_text=self.target_text,
54
+ target_type=self.target_type,
55
+ action="fill",
56
+ value=value
57
+ ))
58
+ return self._mobile
59
+
60
+ class ElementAPI:
61
+ def __init__(self, target: str, context: FlowContext, mobile):
62
+ self.target = target
63
+ self._context = context
64
+ self._mobile = mobile
65
+
66
+ def tap(self):
67
+ self._context.add_command(TapCommand(self.target))
68
+ return self._mobile
69
+
70
+ def fill(self, value: str):
71
+ self._context.add_command(FillCommand(self.target, value))
72
+ return self._mobile
73
+
74
+ # Relational entry points
75
+ def below(self, anchor: str):
76
+ return RelationalAPI(anchor, "below", self._context, self._mobile)
77
+
78
+ def above(self, anchor: str):
79
+ return RelationalAPI(anchor, "above", self._context, self._mobile)
80
+
81
+ def left_of(self, anchor: str):
82
+ return RelationalAPI(anchor, "left_of", self._context, self._mobile)
83
+
84
+ def right_of(self, anchor: str):
85
+ return RelationalAPI(anchor, "right_of", self._context, self._mobile)
86
+
87
+ class Mobile:
88
+ """Core Mobile API for the FlowPilot framework."""
89
+
90
+ def __init__(self):
91
+ self._context = FlowContext()
92
+ self._executor = RuntimeExecutor()
93
+ self.signature = SignatureAPI(self._context)
94
+
95
+ # App Lifecycle
96
+ def launch_app(self, package: str = None):
97
+ self._context.add_command(LaunchAppCommand(package=package))
98
+ return self
99
+
100
+ def close_app(self):
101
+ self._context.add_command(CloseAppCommand())
102
+ return self
103
+
104
+ def restart_app(self):
105
+ self._context.add_command(RestartAppCommand())
106
+ return self
107
+
108
+ # Relational Selectors (Maestro-style)
109
+ def below(self, anchor: str):
110
+ return RelationalAPI(anchor, "below", self._context, self)
111
+
112
+ def above(self, anchor: str):
113
+ return RelationalAPI(anchor, "above", self._context, self)
114
+
115
+ def left_of(self, anchor: str):
116
+ return RelationalAPI(anchor, "left_of", self._context, self)
117
+
118
+ def right_of(self, anchor: str):
119
+ return RelationalAPI(anchor, "right_of", self._context, self)
120
+
121
+ def navigate(self, url: str):
122
+ self._context.add_command(NavigateCommand(url))
123
+ return self
124
+
125
+ # Core Interactions
126
+ def tap(self, target: str):
127
+ """
128
+ Smart Tap:
129
+ 1. Checks visibility.
130
+ 2. Auto-scrolls if not found.
131
+ 3. Retries until timeout.
132
+ """
133
+ self._context.add_command(TapCommand(target))
134
+ return self
135
+
136
+ def fill(self, target: str, value: str):
137
+ """
138
+ Smart Fill:
139
+ 1. Finds label.
140
+ 2. Calculates nearby input field.
141
+ 3. Clears and types safely.
142
+ """
143
+ self._context.add_command(FillCommand(target, value))
144
+ return self
145
+
146
+ def fill_focused(self, value: str):
147
+ """Types text into the currently focused/active input field."""
148
+ self._context.add_command(FillFocusedCommand(value))
149
+ return self
150
+
151
+ def back(self):
152
+ self._context.add_command(BackCommand())
153
+ return self
154
+
155
+ def home(self):
156
+ """Simulates the Home button press."""
157
+ self._context.add_command(HomeCommand())
158
+ return self
159
+
160
+ def hide_keyboard(self):
161
+ self._context.add_command(HideKeyboardCommand())
162
+ return self
163
+
164
+ def press_key(self, key: str):
165
+ self._context.add_command(PressKeyCommand(key))
166
+ return self
167
+
168
+ def clear(self, target: str):
169
+ self._context.add_command(ClearCommand(target))
170
+ return self
171
+
172
+ # Assertions / State Check
173
+ def visible(self, target: str):
174
+ self._context.add_command(VisibleCommand(target))
175
+ return self
176
+
177
+ def not_visible(self, target: str):
178
+ self._context.add_command(NotVisibleCommand(target))
179
+ return self
180
+
181
+ def contains_text(self, text: str):
182
+ self._context.add_command(ContainsTextCommand(text))
183
+ return self
184
+
185
+ # Gestures
186
+ def swipe(self, x1, y1, x2, y2, duration: int = 300):
187
+ self._context.add_command(SwipeCommand(x1, y1, x2, y2, duration))
188
+ return self
189
+
190
+ def open_notifications(self):
191
+ """Swipes top-to-bottom from the left/center to open notifications."""
192
+ self.swipe(200, 10, 200, 1000)
193
+ return self
194
+
195
+ def open_widgets(self):
196
+ """Swipes top-to-bottom from the right side to open widgets/control center."""
197
+ self.swipe(800, 10, 800, 1000)
198
+ return self
199
+
200
+ def open_quick_settings(self):
201
+ """Swipes top-to-bottom from the center to open quick settings/notifications."""
202
+ self.swipe(540, 10, 540, 1000)
203
+ return self
204
+
205
+ def swipe_right(self):
206
+ """Performs a left-to-right horizontal swipe."""
207
+ self.swipe(100, 1000, 900, 1000)
208
+ return self
209
+
210
+ def swipe_left(self):
211
+ """Performs a right-to-left horizontal swipe."""
212
+ self.swipe(900, 1000, 100, 1000)
213
+ return self
214
+
215
+ # Discovery
216
+ def get_screen_text(self) -> list[str]:
217
+ """
218
+ Retrieves all visible text strings from the current screen.
219
+ Forces execution of queued commands first.
220
+ """
221
+ self.execute()
222
+ return self._executor.adb.get_all_text()
223
+
224
+ def find_text(self, text: str) -> bool:
225
+ """Checks if a specific text is currently visible on screen."""
226
+ all_text = self.get_screen_text()
227
+ return any(text.lower() in t.lower() for t in all_text)
228
+
229
+ def list_elements(self, type: str = "button"):
230
+ """
231
+ Returns a list of visible elements of a certain type.
232
+ Used for discovery and smart assertions.
233
+ """
234
+ self.execute()
235
+ return self._executor.adb.get_all_text()
236
+
237
+ # Scrolling
238
+ def scroll_down(self):
239
+ self._context.add_command(ScrollCommand("down"))
240
+ return self
241
+
242
+ def scroll_up(self):
243
+ self._context.add_command(ScrollCommand("up"))
244
+ return self
245
+
246
+ # Waits
247
+ def wait_for(self, condition: str, timeout: int = 30):
248
+ self._context.add_command(WaitCommand(condition, timeout))
249
+ return self
250
+
251
+ # Media / Sensors
252
+ def capture_photo(self):
253
+ self._context.add_command(CapturePhotoCommand())
254
+ return self
255
+
256
+ def screenshot(self, name: str = None):
257
+ self._context.add_command(ScreenshotCommand(name))
258
+ return self
259
+
260
+ def visual_assert(self, baseline_name: str):
261
+ """
262
+ Smart Visual Assertion:
263
+ Compares current screen with a previously saved baseline.
264
+ """
265
+ logger.info(f"Visual Assertion requested: {baseline_name}")
266
+ return self
267
+
268
+ # Fluent Smart Element APIs
269
+ def button(self, name: str) -> ElementAPI:
270
+ return ElementAPI(name, self._context, self)
271
+
272
+ def input(self, name: str) -> ElementAPI:
273
+ return ElementAPI(name, self._context, self)
274
+
275
+ def checkbox(self, name: str) -> ElementAPI:
276
+ return ElementAPI(name, self._context, self)
277
+
278
+ def dropdown(self, name: str) -> ElementAPI:
279
+ return ElementAPI(name, self._context, self)
280
+
281
+ # Execution (Internal or Implicit)
282
+ def execute(self):
283
+ """Forces the execution of the command queue."""
284
+ self._executor.execute(self._context)
File without changes
@@ -0,0 +1,76 @@
1
+ import sys
2
+ from rich.console import Console
3
+ from rich.prompt import Prompt
4
+ from flowpilot.api.mobile import Mobile
5
+ from loguru import logger
6
+
7
+ console = Console()
8
+
9
+ class InteractiveSession:
10
+ """
11
+ A Live REPL for FlowPilot.
12
+ Executes commands on the mobile device line-by-line.
13
+ """
14
+
15
+ def __init__(self):
16
+ self.mobile = Mobile()
17
+ self.history = []
18
+
19
+ def start(self):
20
+ # Disable recording for interactive mode (Step Padding / User Request)
21
+ self.mobile._executor.config.record_video = False
22
+
23
+ console.print("[bold magenta]📱 FlowPilot Interactive Session Started[/bold magenta]")
24
+ console.print("Type your commands (e.g., [cyan]tap(\"Settings\")[/cyan]) and press Enter.")
25
+ console.print("Type [bold red]exit[/bold red] to quit.\n")
26
+
27
+ # Start a persistent session (one recording, one initial reset)
28
+ self.mobile._executor.start_session()
29
+
30
+ while True:
31
+ try:
32
+ user_input = Prompt.ask("[bold green]flowpilot[/bold green]")
33
+
34
+ if user_input.lower() in ["exit", "quit", "q"]:
35
+ console.print("[yellow]Closing interactive session...[/yellow]")
36
+ break
37
+
38
+ if not user_input.strip():
39
+ continue
40
+
41
+ # Prepare the command execution
42
+ # We check if the method exists on mobile
43
+ method_name = user_input.split("(")[0].strip()
44
+ if not hasattr(self.mobile, method_name):
45
+ console.print(f"[bold red]Error:[/bold red] '{method_name}' is not a valid FlowPilot command.")
46
+ continue
47
+
48
+ console.print(f"[dim]Executing: {user_input}...[/dim]")
49
+
50
+ # Execute the command
51
+ # We handle both fluent (returns Mobile) and non-fluent (returns value) calls
52
+ result = eval(f"self.mobile.{user_input}")
53
+
54
+ # If it's a fluent method, we need to call .execute() manually
55
+ # because the REPL is line-by-line.
56
+ from flowpilot.api.mobile import Mobile as MobileClass
57
+ if isinstance(result, MobileClass):
58
+ result.execute()
59
+ else:
60
+ # It was a discovery method like get_screen_text()
61
+ console.print(f"[bold blue]Result:[/bold blue] {result}")
62
+
63
+ self.history.append(user_input)
64
+ console.print("[bold green]✓ Done[/bold green]")
65
+
66
+ except Exception as e:
67
+ console.print(f"[bold red]Execution Error:[/bold red] {e}")
68
+
69
+ # Final cleanup (stop recording, final home reset)
70
+ self.mobile._executor.stop_session()
71
+ console.print("[bold magenta]Session Ended.[/bold magenta]")
72
+
73
+ if self.history:
74
+ console.print(f"\n[bold blue]Summary of your session:[/bold blue]")
75
+ for i, cmd in enumerate(self.history, 1):
76
+ console.print(f"{i}. {cmd}")