connectonion 0.6.1__py3-none-any.whl → 0.6.2__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.
@@ -1,4 +1,4 @@
1
- connectonion/__init__.py,sha256=7geVck4xTtsy0x9UbmCpSuFaCzaTTiT5PgHHXIGUKLU,1958
1
+ connectonion/__init__.py,sha256=BWZ4QU8IPkEyyNHXEhLRJ0M2XEabP3qx4RoPWVMwlD8,1958
2
2
  connectonion/address.py,sha256=YOzpMOej-HqJUE6o0i0fG8rB7HM-Iods36s9OD--5ig,10852
3
3
  connectonion/console.py,sha256=zmnhavw359iqY9HFI1gbZolRSqiDlzJPEkwI1CllWfY,21239
4
4
  connectonion/llm_do.py,sha256=rwgSsTreNGAq5xV3m9lbA7U5AE0XOZNdihJwW5FHz0k,12005
@@ -9,9 +9,15 @@ connectonion/cli/__init__.py,sha256=Pd1iKp0rUghs2kmdAhyp9hK8j0uWkNxOagBkdCGrG4I,
9
9
  connectonion/cli/docs.md,sha256=Fk_JT8jFXXDpXTvU0ZUigMW1UR6ERmX-HDheYPPRNY8,3231
10
10
  connectonion/cli/main.py,sha256=ys0L_zwV4lkrln_86XrJ_U_1ARQ6oxoADtHWj3tSIkg,7315
11
11
  connectonion/cli/browser_agent/__init__.py,sha256=xZCoxS3dGVBBMnaasHOE1vxMDdIwUloRP67rGR-4obo,173
12
- connectonion/cli/browser_agent/browser.py,sha256=P3ceJrHVS63NJvuQltjeOy4rZj1H-8kDy-qOZSWK9AA,21532
13
- connectonion/cli/browser_agent/prompt.md,sha256=tF0PhGcl3gkmVK1F5SG9GZwnPPXTZYWEaatAZtOYZZg,4479
14
- connectonion/cli/browser_agent/scroll_strategies.py,sha256=t_noSlzbEptgBiRWgrlIv4L_jv33xHvKgxbg1qUFDL4,10109
12
+ connectonion/cli/browser_agent/browser.py,sha256=f31-wC4JxZ_S8lLhyH5BXfHy-LvkOKhO-fhSOigzclQ,19800
13
+ connectonion/cli/browser_agent/element_finder.py,sha256=iAwF-y5NrL2l_H13VOC9kX9ouupJLJETvHCq8WyyQs0,4024
14
+ connectonion/cli/browser_agent/highlight_screenshot.py,sha256=wkR2IcPPGspnFq1AVJTc6QIztUXX3orluOKtA58rgpc,5127
15
+ connectonion/cli/browser_agent/prompt.md,sha256=1lBK6O4pq-LUa_9f6J2FaBncrM8SBF8YVNdbH7Njaq8,5891
16
+ connectonion/cli/browser_agent/scroll.py,sha256=yA-DED0EelCqX3ucTi6C830R7BBBt1qqMKY-bUrqiUI,4374
17
+ connectonion/cli/browser_agent/prompts/element_matcher.md,sha256=oblcwJ7OuQ8t0dF-dJh--QtdLTyjYJr3cBLWpaCs0N0,1706
18
+ connectonion/cli/browser_agent/prompts/form_filler.md,sha256=r4Trnln51rjKTIYGJ9Svu4e4XoDwT0lNeF3otxS537I,423
19
+ connectonion/cli/browser_agent/prompts/scroll_strategy.md,sha256=fbvEtMM4J9yhGXNeDdKCf4D5ZB5fA-KQrLapmul0wVU,833
20
+ connectonion/cli/browser_agent/scripts/extract_elements.js,sha256=0YMufRSeBf6PQLxbpgVmHvlnjPVNTYqmmsuWyOZBGNc,4651
15
21
  connectonion/cli/commands/__init__.py,sha256=IPZy9NwrE0hs4f7hIqyFM9GjFZpeCS8mC0Jf-5Ooy4c,43
16
22
  connectonion/cli/commands/auth_commands.py,sha256=D76_0yd77d23bXRvsTAY6HOcGJswo9-6z2dRi9CR9sE,21635
17
23
  connectonion/cli/commands/browser_commands.py,sha256=lB4N6XwP43qdcthwQWlbFU2S3INfxhRDXznAw1PSTsQ,1803
@@ -117,7 +123,7 @@ connectonion/useful_tools/slash_command.py,sha256=VKl7SKLyaxHcHwfE8rgzBCQ2-KQtL0
117
123
  connectonion/useful_tools/terminal.py,sha256=PtzGHN6vnWROmssi37YOZ5U-d0Z7Fe79bocoKAngoxg,9330
118
124
  connectonion/useful_tools/todo_list.py,sha256=wVEt4kt-MczIcP3xvKelfshGHZ6EATpDFzQx0zov8cw,7483
119
125
  connectonion/useful_tools/web_fetch.py,sha256=CysJLOdDIkbMVJ12Dj3WgsytLu1-o2wrwNopqA_S1jk,7253
120
- connectonion-0.6.1.dist-info/METADATA,sha256=UfECn-oR2IqKWVKkgNwbOXKQlt_SymSu7O7pkMoNIR0,21932
121
- connectonion-0.6.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
122
- connectonion-0.6.1.dist-info/entry_points.txt,sha256=XDB-kVN7Qgy4DmYTkjQB_O6hZeUND-SqmZbdoQPn6WA,90
123
- connectonion-0.6.1.dist-info/RECORD,,
126
+ connectonion-0.6.2.dist-info/METADATA,sha256=9nm3n8-43pLq0POgztJ69leg6DvszoOwVltbiTu1RVo,21932
127
+ connectonion-0.6.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
128
+ connectonion-0.6.2.dist-info/entry_points.txt,sha256=XDB-kVN7Qgy4DmYTkjQB_O6hZeUND-SqmZbdoQPn6WA,90
129
+ connectonion-0.6.2.dist-info/RECORD,,
@@ -1,276 +0,0 @@
1
- """
2
- Purpose: Universal scrolling strategies with AI-powered selection and screenshot-based verification
3
- LLM-Note:
4
- Dependencies: imports from [typing, pydantic, connectonion.llm_do, PIL.Image, os, time] | imported by [web_automation.py] | tested by [tests/test_final_scroll.py]
5
- Data flow: receives page: Page, take_screenshot: Callable, times: int, description: str from web_automation.scroll() → scroll_with_verification() orchestrates 3 strategies → ai_scroll_strategy() calls llm_do(HTML+scrollable_elements→ScrollStrategy, gpt-4o) → element_scroll_strategy()/page_scroll_strategy() fallbacks → page.evaluate(javascript) executes scroll → screenshots_are_different() compares PIL Images with 1% pixel threshold → returns success/failure string
6
- State/Effects: calls page.evaluate() multiple times (mutates DOM scroll positions) | take_screenshot() writes PNG files to screenshots/*.png | time.sleep(1-1.2) between scroll iterations | AI calls to gpt-4o with temperature=0.1 for strategy generation
7
- Integration: exposes scroll_with_verification() as main entry point from WebAutomation.scroll() | exposes scroll_page(), scroll_element() as standalone utilities | ScrollStrategy Pydantic model defines AI output schema (javascript: str, explanation: str) | screenshots_are_different() uses PIL for pixel-level comparison
8
- Performance: ai_scroll_strategy() calls llm_do() once per scroll session (100-500ms) | analyzes first 5000 chars of HTML | finds up to 3 scrollable elements | executes JS times iterations with 1.2s delays | element/page strategies are synchronous JS execution (fast) | PIL screenshot comparison ~50-100ms
9
- Errors: returns descriptive strings (not exceptions) - "All scroll strategies failed", "Browser not open" | screenshot comparison failure returns True (assumes different) to continue | page.evaluate() exceptions caught and next strategy tried | prints debug output to stdout
10
- ⚠️ Strategy order: AI-first may be slower but more accurate for complex sites (Gmail) - reorder if speed critical
11
- ⚠️ Screenshot verification: 1% threshold may need tuning for high-resolution displays or subtle animations
12
- """
13
-
14
- from typing import Callable, List, Tuple
15
- from pydantic import BaseModel
16
- from connectonion import llm_do
17
-
18
-
19
- class ScrollStrategy(BaseModel):
20
- """AI-generated scroll strategy."""
21
- javascript: str
22
- explanation: str
23
-
24
-
25
- def scroll_with_verification(
26
- page,
27
- take_screenshot: Callable,
28
- times: int = 5,
29
- description: str = "the main content area"
30
- ) -> str:
31
- """Universal scroll with automatic strategy selection and fallback.
32
-
33
- Tries multiple strategies in order until one works:
34
- 1. AI-generated strategy (default)
35
- 2. Element scrolling
36
- 3. Page scrolling
37
-
38
- Args:
39
- page: Playwright page object
40
- take_screenshot: Function to take screenshots
41
- times: Number of scroll iterations
42
- description: What to scroll (natural language)
43
-
44
- Returns:
45
- Status message with successful strategy
46
- """
47
- if not page:
48
- return "Browser not open"
49
-
50
- print(f"\n📜 Starting universal scroll for: '{description}'")
51
-
52
- import time
53
- timestamp = int(time.time())
54
- before_file = f"scroll_before_{timestamp}.png"
55
- after_file = f"scroll_after_{timestamp}.png"
56
-
57
- # Take before screenshot
58
- take_screenshot(before_file)
59
-
60
- strategies = [
61
- ("AI-generated strategy", lambda: ai_scroll_strategy(page, times, description)),
62
- ("Element scrolling", lambda: element_scroll_strategy(page, times)),
63
- ("Page scrolling", lambda: page_scroll_strategy(page, times))
64
- ]
65
-
66
- for strategy_name, strategy_func in strategies:
67
- print(f"\n Trying: {strategy_name}...")
68
-
69
- try:
70
- strategy_func()
71
- time.sleep(1)
72
-
73
- # Take after screenshot
74
- take_screenshot(after_file)
75
-
76
- # Verify scroll worked
77
- if screenshots_are_different(before_file, after_file):
78
- print(f" ✅ {strategy_name} WORKED! Content changed.")
79
- return f"Scroll successful using {strategy_name}. Check {before_file} vs {after_file}"
80
- else:
81
- print(f" ⚠️ {strategy_name} didn't change content. Trying next...")
82
- before_file = after_file
83
- after_file = f"scroll_after_{timestamp}_next.png"
84
-
85
- except Exception as e:
86
- print(f" ❌ {strategy_name} failed: {e}")
87
- continue
88
-
89
- return "All scroll strategies failed. No visible content change."
90
-
91
-
92
- def screenshots_are_different(file1: str, file2: str) -> bool:
93
- """Compare screenshots to verify content changed.
94
-
95
- Args:
96
- file1: First screenshot filename
97
- file2: Second screenshot filename
98
-
99
- Returns:
100
- True if screenshots are different
101
- """
102
- try:
103
- from PIL import Image
104
- import os
105
-
106
- path1 = os.path.join("screenshots", file1)
107
- path2 = os.path.join("screenshots", file2)
108
-
109
- img1 = Image.open(path1).convert('RGB')
110
- img2 = Image.open(path2).convert('RGB')
111
-
112
- # Calculate pixel difference
113
- diff = sum(
114
- abs(a - b)
115
- for pixel1, pixel2 in zip(img1.getdata(), img2.getdata())
116
- for a, b in zip(pixel1, pixel2)
117
- )
118
-
119
- # 1% threshold
120
- threshold = img1.size[0] * img1.size[1] * 3 * 0.01
121
-
122
- is_different = diff > threshold
123
- print(f" Screenshot diff: {diff:.0f} (threshold: {threshold:.0f}) - {'DIFFERENT' if is_different else 'SAME'}")
124
-
125
- return is_different
126
-
127
- except Exception as e:
128
- print(f" Warning: Screenshot comparison failed: {e}")
129
- return True # Assume different if comparison fails
130
-
131
-
132
- def ai_scroll_strategy(page, times: int, description: str):
133
- """AI-generated scroll strategy.
134
-
135
- Analyzes page structure and generates custom JavaScript.
136
- """
137
- # Find scrollable elements
138
- scrollable_elements = page.evaluate("""
139
- (() => {
140
- const scrollable = [];
141
- document.querySelectorAll('*').forEach(el => {
142
- const style = window.getComputedStyle(el);
143
- if ((style.overflow === 'auto' || style.overflowY === 'scroll') &&
144
- el.scrollHeight > el.clientHeight) {
145
- scrollable.push({
146
- tag: el.tagName,
147
- classes: el.className,
148
- id: el.id
149
- });
150
- }
151
- });
152
- return scrollable;
153
- })()
154
- """)
155
-
156
- # Get simplified HTML
157
- simplified_html = page.evaluate("""
158
- (() => {
159
- const clone = document.body.cloneNode(true);
160
- clone.querySelectorAll('script, style, img, svg').forEach(el => el.remove());
161
- return clone.innerHTML.substring(0, 5000);
162
- })()
163
- """)
164
-
165
- # Generate scroll strategy using AI
166
- strategy = llm_do(
167
- f"""Generate JavaScript to scroll "{description}".
168
-
169
- Scrollable elements: {scrollable_elements[:3]}
170
- HTML structure: {simplified_html}
171
-
172
- Return IIFE that scrolls the correct element:
173
- (() => {{
174
- const el = document.querySelector('.selector');
175
- if (el) el.scrollTop += 1000;
176
- return {{success: true}};
177
- }})()
178
- """,
179
- output=ScrollStrategy,
180
- model="gpt-4o",
181
- temperature=0.1
182
- )
183
-
184
- print(f" AI generated: {strategy.explanation}")
185
-
186
- # Execute scroll
187
- import time
188
- for i in range(times):
189
- page.evaluate(strategy.javascript)
190
- time.sleep(1.2)
191
-
192
-
193
- def element_scroll_strategy(page, times: int):
194
- """Scroll first scrollable element found."""
195
- import time
196
- for i in range(times):
197
- page.evaluate("""
198
- (() => {
199
- const el = Array.from(document.querySelectorAll('*')).find(e => {
200
- const s = window.getComputedStyle(e);
201
- return (s.overflow === 'auto' || s.overflowY === 'scroll') &&
202
- e.scrollHeight > e.clientHeight;
203
- });
204
- if (el) el.scrollTop += 1000;
205
- })()
206
- """)
207
- time.sleep(1)
208
-
209
-
210
- def page_scroll_strategy(page, times: int):
211
- """Scroll the page window."""
212
- import time
213
- for i in range(times):
214
- page.evaluate("window.scrollBy(0, 1000)")
215
- time.sleep(1)
216
-
217
-
218
- # Additional scroll helpers that can be called directly
219
- def scroll_page(page, direction: str = "down", amount: int = 1000) -> str:
220
- """Scroll the page in a specific direction.
221
-
222
- Args:
223
- page: Playwright page object
224
- direction: "down", "up", "top", or "bottom"
225
- amount: Pixels to scroll
226
-
227
- Returns:
228
- Status message
229
- """
230
- if not page:
231
- return "Browser not open"
232
-
233
- if direction == "bottom":
234
- page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
235
- return "Scrolled to bottom of page"
236
- elif direction == "top":
237
- page.evaluate("window.scrollTo(0, 0)")
238
- return "Scrolled to top of page"
239
- elif direction == "down":
240
- page.evaluate(f"window.scrollBy(0, {amount})")
241
- return f"Scrolled down {amount} pixels"
242
- elif direction == "up":
243
- page.evaluate(f"window.scrollBy(0, -{amount})")
244
- return f"Scrolled up {amount} pixels"
245
- else:
246
- return f"Unknown direction: {direction}"
247
-
248
-
249
- def scroll_element(page, selector: str, amount: int = 1000) -> str:
250
- """Scroll a specific element by CSS selector.
251
-
252
- Args:
253
- page: Playwright page object
254
- selector: CSS selector for the element
255
- amount: Pixels to scroll
256
-
257
- Returns:
258
- Status message
259
- """
260
- if not page:
261
- return "Browser not open"
262
-
263
- result = page.evaluate(f"""
264
- (() => {{
265
- const element = document.querySelector('{selector}');
266
- if (!element) return 'Element not found: {selector}';
267
-
268
- const beforeScroll = element.scrollTop;
269
- element.scrollTop += {amount};
270
- const afterScroll = element.scrollTop;
271
-
272
- return `Scrolled from ${{beforeScroll}}px to ${{afterScroll}}px (delta: ${{afterScroll - beforeScroll}}px)`;
273
- }})()
274
- """)
275
-
276
- return result