simplex 1.2.62__tar.gz → 1.2.64__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.

Potentially problematic release.


This version of simplex might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.1
2
2
  Name: simplex
3
- Version: 1.2.62
3
+ Version: 1.2.64
4
4
  Summary: Official Python SDK for Simplex API
5
5
  Home-page: https://simplex.sh
6
6
  Author: Simplex Labs, Inc.
@@ -19,16 +19,6 @@ Requires-Dist: python-dotenv
19
19
  Requires-Dist: click
20
20
  Provides-Extra: playwright
21
21
  Requires-Dist: playwright>=1.0.0; extra == "playwright"
22
- Dynamic: author
23
- Dynamic: author-email
24
- Dynamic: classifier
25
- Dynamic: description
26
- Dynamic: description-content-type
27
- Dynamic: home-page
28
- Dynamic: provides-extra
29
- Dynamic: requires-dist
30
- Dynamic: requires-python
31
- Dynamic: summary
32
22
 
33
23
  # Simplex AI Python SDK
34
24
 
@@ -22,7 +22,7 @@ class PostInstallCommand(install):
22
22
 
23
23
  setup(
24
24
  name="simplex",
25
- version="1.2.62",
25
+ version="1.2.64",
26
26
  packages=find_packages(),
27
27
  package_data={
28
28
  "simplex": ["browser_agent/dom/*.js"], # Include JS files in the dom directory
@@ -5,20 +5,30 @@ import os
5
5
  import json
6
6
  import warnings
7
7
  import time
8
+ import tempfile
8
9
  # Try to import playwright, but don't fail if it's not available (lite version)
9
10
  try:
10
- from playwright.sync_api import sync_playwright
11
+ from rebrowser_playwright.sync_api import sync_playwright
11
12
  PLAYWRIGHT_AVAILABLE = True
12
13
  except ImportError:
13
- PLAYWRIGHT_AVAILABLE = False
14
- warnings.warn(
15
- "Playwright is not available. Some features will be disabled. "
16
- "To enable all features, install with 'pip install simplex[playwright]'",
17
- ImportWarning
18
- )
19
-
20
- #BASE_URL = "https://simplex-dev--api-server-fastapi-app.modal.run"
14
+ try:
15
+ from playwright.sync_api import sync_playwright
16
+ PLAYWRIGHT_AVAILABLE = True
17
+ warnings.warn(
18
+ "Using standard playwright instead of rebrowser-patches. Some anti-bot systems may detect automation. "
19
+ "To improve stealth, install with 'pip install rebrowser-playwright'",
20
+ ImportWarning
21
+ )
22
+ except ImportError:
23
+ PLAYWRIGHT_AVAILABLE = False
24
+ warnings.warn(
25
+ "Playwright is not available. Some features will be disabled. "
26
+ "To enable all features, install with 'pip install simplex[playwright]'",
27
+ ImportWarning
28
+ )
29
+
21
30
  BASE_URL = "https://api.simplex.sh"
31
+
22
32
  class Playwright:
23
33
  def __init__(self, simplex_instance):
24
34
  self.simplex = simplex_instance
@@ -58,7 +68,6 @@ class Playwright:
58
68
  )
59
69
 
60
70
  if 'succeeded' not in response.json():
61
- print(response.json())
62
71
  raise ValueError(f"It looks like the click action with playwright failed to return a response. Did you set your api_key when creating the Simplex class?")
63
72
 
64
73
  if "succeeded" in response.json():
@@ -111,6 +120,7 @@ class Simplex:
111
120
  raise ValueError(f"Failed to close session: {response.json()['error']}")
112
121
 
113
122
  def create_session(self, show_in_console: Optional[bool] = True, proxies: Optional[bool] = True, workflow_name: Optional[str] = None, session_data: Optional[dict | str] = None):
123
+ print("CREATING SESSION")
114
124
  if self.session_id:
115
125
  raise ValueError("A session is already active. Please close the current session before creating a new one.")
116
126
 
@@ -187,6 +197,7 @@ class Simplex:
187
197
  },
188
198
  data=data
189
199
  )
200
+
190
201
  if 'succeeded' not in response.json():
191
202
  raise ValueError(f"It looks like the goto action failed to return a response. Did you set your api_key when creating the Simplex class?")
192
203
 
@@ -222,6 +233,65 @@ class Simplex:
222
233
  else:
223
234
  raise ValueError(f"Failed to click element: {response.json()['error']}")
224
235
 
236
+
237
+ def scroll_to_element(self, element_description: str, cdp_url: str = None):
238
+ if not element_description or not element_description.strip():
239
+ raise ValueError("element_description cannot be empty")
240
+ if not cdp_url and not self.session_id:
241
+ raise ValueError(f"Must call create_session before calling action scroll_to_element with element_description='{element_description}'")
242
+
243
+ data = {'element_description': element_description}
244
+
245
+ if cdp_url:
246
+ data['cdp_url'] = cdp_url
247
+ else:
248
+ data['session_id'] = self.session_id
249
+
250
+ response = requests.post(
251
+ f"{BASE_URL}/scroll_to_element",
252
+ headers={
253
+ 'x-api-key': self.api_key
254
+ },
255
+ data=data
256
+ )
257
+
258
+ if 'succeeded' not in response.json():
259
+ raise ValueError(f"It looks like the scroll_to_element action failed to return a response. Did you set your api_key when creating the Simplex class?")
260
+ if response.json()["succeeded"]:
261
+ return
262
+ else:
263
+ raise ValueError(f"Failed to scroll element into view: {response.json()['error']}")
264
+
265
+
266
+
267
+ def hover(self, element_description: str, cdp_url: str = None):
268
+ if not element_description or not element_description.strip():
269
+ raise ValueError("element_description cannot be empty")
270
+ if not cdp_url and not self.session_id:
271
+ raise ValueError(f"Must call create_session before calling action hover with element_description='{element_description}'")
272
+
273
+ data = {'element_description': element_description}
274
+
275
+ if cdp_url:
276
+ data['cdp_url'] = cdp_url
277
+ else:
278
+ data['session_id'] = self.session_id
279
+
280
+ response = requests.post(
281
+ f"{BASE_URL}/hover",
282
+ headers={
283
+ 'x-api-key': self.api_key
284
+ },
285
+ data=data
286
+ )
287
+ if 'succeeded' not in response.json():
288
+ raise ValueError(f"It looks like the hover action failed to return a response. Did you set your api_key when creating the Simplex class?")
289
+
290
+ if response.json()["succeeded"]:
291
+ return
292
+ else:
293
+ raise ValueError(f"Failed to hover: {response.json()['error']}")
294
+
225
295
  def type(self, text: str, cdp_url: str = None):
226
296
  if not text or not text.strip():
227
297
  raise ValueError("text cannot be empty")
@@ -245,7 +315,6 @@ class Simplex:
245
315
  if "succeeded" in response.json():
246
316
  return
247
317
  else:
248
- print(response.json())
249
318
  raise ValueError(f"Failed to type text: {response.json()['error']}")
250
319
 
251
320
 
@@ -504,42 +573,60 @@ class Simplex:
504
573
  ],
505
574
  ignore_default_args=['--enable-automation']
506
575
  )
507
-
508
576
  # Create context and pages
509
- context = browser.new_context()
577
+ context = browser.new_context(viewport=None)
510
578
  main_page = context.new_page()
511
579
  main_page.goto(url)
512
580
 
513
581
  # Create control page in same context
514
582
  control_page = context.new_page()
515
- control_page.set_content("""
516
- <html>
517
- <body style="display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f5f5f5;">
518
- <button id="capture-btn" style="
519
- padding: 20px 40px;
520
- font-size: 18px;
521
- cursor: pointer;
522
- background-color: #2196F3;
523
- color: white;
524
- border: none;
525
- border-radius: 5px;
526
- box-shadow: 0 2px 4px rgba(0,0,0,0.2);
527
- ">Click here when logged in to capture session</button>
528
- </body>
529
- </html>
530
- """)
583
+ # Use a simple HTML file instead of set_content
584
+ html_content = """
585
+ <!DOCTYPE html>
586
+ <html>
587
+ <head>
588
+ <title>Capture Session</title>
589
+ </head>
590
+ <body style="display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f5f5f5;">
591
+ <button id="capture-btn" style="
592
+ padding: 20px 40px;
593
+ font-size: 18px;
594
+ cursor: pointer;
595
+ background-color: #2196F3;
596
+ color: white;
597
+ border: none;
598
+ border-radius: 5px;
599
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2);
600
+ ">Click here when logged in to capture session</button>
601
+ </body>
602
+ </html>
603
+ """
604
+ # Write to a temporary file
605
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.html', mode='w') as f:
606
+ f.write(html_content)
607
+ temp_path = f.name
531
608
 
532
- # Wait for button click using Playwright's wait_for_event
533
- control_page.wait_for_selector('#capture-btn')
534
- control_page.evaluate("""
535
- () => {
536
- return new Promise((resolve) => {
537
- document.getElementById('capture-btn').onclick = () => {
538
- resolve(true);
539
- }
540
- });
541
- }
542
- """)
609
+ try:
610
+ # Load from file instead of set_content
611
+ control_page.goto(f"file://{temp_path}")
612
+ control_page.wait_for_selector('#capture-btn', timeout=30000)
613
+
614
+ # Wait for button click
615
+ control_page.evaluate("""
616
+ () => {
617
+ return new Promise((resolve) => {
618
+ document.getElementById('capture-btn').onclick = () => {
619
+ resolve(true);
620
+ }
621
+ });
622
+ }
623
+ """)
624
+ finally:
625
+ # Clean up temp file
626
+ try:
627
+ os.unlink(temp_path)
628
+ except:
629
+ pass
543
630
 
544
631
  # Use context.storage_state() instead of browser.storage_state()
545
632
  storage = context.storage_state()
@@ -556,7 +643,34 @@ class Simplex:
556
643
  browser.close()
557
644
 
558
645
  return filename
559
-
646
+
647
+ def get_network_response(self, url: str, cdp_url: str = None):
648
+ print(f"Getting network response for {url}")
649
+ if not cdp_url and not self.session_id:
650
+ raise ValueError(f"Must call create_session before calling action get_network_response with url='{url}'")
651
+
652
+ data = {'url': url}
653
+
654
+ if cdp_url:
655
+ data['cdp_url'] = cdp_url
656
+ else:
657
+ data['session_id'] = self.session_id
658
+
659
+ response = requests.post(
660
+ f"{BASE_URL}/get_network_response",
661
+ headers={
662
+ 'x-api-key': self.api_key
663
+ },
664
+ data=data
665
+ )
666
+
667
+ if 'status' not in response.json():
668
+ raise ValueError(f"It looks like the get_network_response action failed to return a response. Did you set your api_key when creating the Simplex class?")
669
+ if response.json()['status'] == 'success':
670
+ return response.json()['response']
671
+ else:
672
+ raise ValueError(f"Failed to get network response: {response.json()['error']}")
673
+
560
674
  def restore_login_session(self, session_data: str, cdp_url: str = None):
561
675
  """
562
676
  Restore a login session from either a file path or a JSON string.
@@ -664,9 +778,20 @@ class Simplex:
664
778
  # Get filename from Content-Disposition header
665
779
  content_disposition = response.headers.get('Content-Disposition')
666
780
  if content_disposition:
667
- filename = content_disposition.split('filename=')[1].strip('"')
668
- else:
669
- raise ValueError("No filename found in Content-Disposition header. File was not downloaded.")
781
+ try:
782
+ filename = content_disposition.split('filename=')[1].strip('"')
783
+ except:
784
+ try:
785
+ filename_star = content_disposition.split('filename*=')[1].split(';')[0]
786
+ # Parse the RFC 5987 encoded filename
787
+ encoding, _, fname = filename_star.split("'", 2)
788
+ # URL decode the filename
789
+ from urllib.parse import unquote
790
+ filename = unquote(fname)
791
+ except Exception:
792
+ raise ValueError("File failed to be parsed from Content-Disposition header.")
793
+ else:
794
+ raise ValueError("No Content-Disposition header found. File was not downloaded.")
670
795
 
671
796
  # Check if response content is empty
672
797
  if len(response.content) == 0:
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.1
2
2
  Name: simplex
3
- Version: 1.2.62
3
+ Version: 1.2.64
4
4
  Summary: Official Python SDK for Simplex API
5
5
  Home-page: https://simplex.sh
6
6
  Author: Simplex Labs, Inc.
@@ -19,16 +19,6 @@ Requires-Dist: python-dotenv
19
19
  Requires-Dist: click
20
20
  Provides-Extra: playwright
21
21
  Requires-Dist: playwright>=1.0.0; extra == "playwright"
22
- Dynamic: author
23
- Dynamic: author-email
24
- Dynamic: classifier
25
- Dynamic: description
26
- Dynamic: description-content-type
27
- Dynamic: home-page
28
- Dynamic: provides-extra
29
- Dynamic: requires-dist
30
- Dynamic: requires-python
31
- Dynamic: summary
32
22
 
33
23
  # Simplex AI Python SDK
34
24
 
File without changes
File without changes
File without changes
File without changes
File without changes