repld-tool 0.2.2__tar.gz → 0.2.3__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 (63) hide show
  1. {repld_tool-0.2.2 → repld_tool-0.2.3}/PKG-INFO +1 -1
  2. {repld_tool-0.2.2 → repld_tool-0.2.3}/pyproject.toml +1 -1
  3. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/browser/tab.py +39 -0
  4. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/help.py +9 -0
  5. {repld_tool-0.2.2 → repld_tool-0.2.3}/LICENSE +0 -0
  6. {repld_tool-0.2.2 → repld_tool-0.2.3}/README.md +0 -0
  7. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/.ruff_cache/.gitignore +0 -0
  8. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/.ruff_cache/0.15.22/14226964882126921391 +0 -0
  9. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/.ruff_cache/CACHEDIR.TAG +0 -0
  10. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/__init__.py +0 -0
  11. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/__main__.py +0 -0
  12. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/bg.py +0 -0
  13. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/bind.py +0 -0
  14. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/bridge.py +0 -0
  15. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/bridge_tools.py +0 -0
  16. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/browser/__init__.py +0 -0
  17. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/browser/browser.py +0 -0
  18. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/browser/capture.py +0 -0
  19. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/browser/cdp.py +0 -0
  20. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/browser/har.py +0 -0
  21. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/browser/observe.py +0 -0
  22. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/browser/pin.py +0 -0
  23. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/browser/png.py +0 -0
  24. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/browser/pool.py +0 -0
  25. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/browser/row.py +0 -0
  26. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/browser/selector.py +0 -0
  27. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/browser/session.py +0 -0
  28. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/browser/tab_query.py +0 -0
  29. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/browser/target.py +0 -0
  30. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/browser_dispatch.py +0 -0
  31. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/channel.py +0 -0
  32. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/cli.py +0 -0
  33. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/cli_args.py +0 -0
  34. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/core_schemas.py +0 -0
  35. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/dashboard.py +0 -0
  36. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/dashboard_cmd.py +0 -0
  37. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/dashboard_html.py +0 -0
  38. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/display.py +0 -0
  39. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/eventlog.py +0 -0
  40. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/events.py +0 -0
  41. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/exec_cmd.py +0 -0
  42. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/gate_cmd.py +0 -0
  43. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/gates.py +0 -0
  44. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/gist_api.py +0 -0
  45. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/gist_cmd.py +0 -0
  46. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/gist_deps.py +0 -0
  47. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/gist_links.py +0 -0
  48. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/gist_lint.py +0 -0
  49. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/gists.py +0 -0
  50. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/ipc.py +0 -0
  51. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/kernel.py +0 -0
  52. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/kernel_context.py +0 -0
  53. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/lifecycle_cmd.py +0 -0
  54. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/log_cmd.py +0 -0
  55. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/paths.py +0 -0
  56. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/protocol.py +0 -0
  57. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/relaunch.py +0 -0
  58. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/render.py +0 -0
  59. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/runtime.py +0 -0
  60. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/sessions.py +0 -0
  61. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/spawn.py +0 -0
  62. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/state.py +0 -0
  63. {repld_tool-0.2.2 → repld_tool-0.2.3}/src/repld/tasks.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: repld-tool
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Persistent Python runtime with MCP channel push. Dev shell and autonomous-agent substrate in one package.
5
5
  Keywords: repl,mcp,agent,browser
6
6
  Author: Fredrik Angelsen
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "repld-tool"
3
- version = "0.2.2"
3
+ version = "0.2.3"
4
4
  description = "Persistent Python runtime with MCP channel push. Dev shell and autonomous-agent substrate in one package."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -481,6 +481,40 @@ class Tab(TabQueryMixin):
481
481
  ) from reattach_exc
482
482
  return await self._session.execute(method, params, timeout)
483
483
 
484
+ async def _ensure_front(self) -> None:
485
+ """Raise this tab if Chrome has it backgrounded, before dispatching input.
486
+
487
+ `click`/`tap`/`type_text`/`key` all ride `Input.dispatch*Event` —
488
+ the renderer's real input pipeline, which Chrome silently drops for
489
+ a tab whose window is occluded or backgrounded
490
+ (`document.visibilityState === "hidden"`): the CDP command still
491
+ acks, no exception is raised, and no handler on the page ever
492
+ fires. Resolving a selector doesn't hit this — `DOM.getContentQuads`
493
+ and `Runtime.evaluate` both work fine on a hidden tab, which is why
494
+ the failure looks like nothing happened rather than an error. Costs
495
+ one `Runtime.evaluate` round trip on every call and `Page.bringToFront`
496
+ only on the degraded path — the multi-window watch workflow this
497
+ kernel exists for means N-1 tabs are hidden at any given time, so
498
+ this can't be opt-in.
499
+ """
500
+ result = await self._exec(
501
+ "Runtime.evaluate",
502
+ {"expression": "document.visibilityState", "returnByValue": True},
503
+ )
504
+ if result.get("result", {}).get("value") == "hidden":
505
+ await self._exec("Page.bringToFront")
506
+
507
+ async def front(self) -> None:
508
+ """Bring this tab's window to the front (Page.bringToFront).
509
+
510
+ click()/tap()/type_text()/key() already do this automatically when
511
+ the tab is backgrounded — call this directly to raise a tab for its
512
+ own sake (e.g. before a screenshot a human is watching for), or to
513
+ avoid paying the visibility check on every call in a script that
514
+ knows it's about to do a lot of input against one tab.
515
+ """
516
+ await self._exec("Page.bringToFront")
517
+
484
518
  # ------------------------------------------------------------------
485
519
 
486
520
  async def js(
@@ -643,6 +677,7 @@ class Tab(TabQueryMixin):
643
677
 
644
678
  Selector: CSS, text=Label, role=button[name='OK'], or tag:has-text('...')
645
679
  """
680
+ await self._ensure_front()
646
681
  x, y = await self._element_center(selector)
647
682
 
648
683
  for event_type in ("mousePressed", "mouseReleased"):
@@ -670,6 +705,7 @@ class Tab(TabQueryMixin):
670
705
  Selects all existing content then types over it.
671
706
  Selector: CSS, text=Label, role=textbox, label=Name, or tag:has-text('...')
672
707
  """
708
+ await self._ensure_front()
673
709
  node_id, js_expr = await self._wait_for_node(selector)
674
710
 
675
711
  if node_id:
@@ -712,6 +748,7 @@ class Tab(TabQueryMixin):
712
748
 
713
749
  async def key(self, key: str) -> None:
714
750
  """Dispatch a keyDown+keyUp pair for a named key (e.g. "Enter", "Escape")."""
751
+ await self._ensure_front()
715
752
  for event_type in ("keyDown", "keyUp"):
716
753
  await self._exec(
717
754
  "Input.dispatchKeyEvent",
@@ -735,6 +772,7 @@ class Tab(TabQueryMixin):
735
772
  Uses Input.dispatchTouchEvent — triggers touchstart/touchend listeners
736
773
  that dispatchMouseEvent won't reach. Use for mobile Chrome via ADB.
737
774
  """
775
+ await self._ensure_front()
738
776
  if y is not None:
739
777
  x, y = float(selector_or_x), y
740
778
  else:
@@ -759,6 +797,7 @@ class Tab(TabQueryMixin):
759
797
  Dispatches touchStart → touchMove × steps → touchEnd.
760
798
  For scrolling on mobile Chrome via ADB.
761
799
  """
800
+ await self._ensure_front()
762
801
  await self._touch("touchStart", [{"x": x1, "y": y1}])
763
802
  delay = duration_ms / steps / 1000
764
803
  for i in range(1, steps + 1):
@@ -482,6 +482,14 @@ Convention: add data-testid to your root layout component.
482
482
  3s timeout — raises TimeoutError if the page's touch handler blocks
483
483
  (common on complex apps like Messenger/React).
484
484
 
485
+ tab.front() → None
486
+ Bring this tab's window to the front (Page.bringToFront).
487
+ click/tap/type_text/key already call this automatically when Chrome
488
+ has the tab backgrounded — coordinate/key input silently does nothing
489
+ on a hidden tab otherwise (Chrome drops it, no error). Call directly
490
+ to raise a tab for its own sake, or to skip the per-call visibility
491
+ check in a script doing a lot of input against one tab.
492
+
485
493
  tab.swipe(x1, y1, x2, y2, *, steps=10, duration_ms=300) → None
486
494
  Touch swipe: touchStart → touchMove × steps → touchEnd.
487
495
  For scrolling on mobile Chrome via ADB.
@@ -1152,6 +1160,7 @@ Tab (async unless noted):
1152
1160
  tab.tree() → list[str]
1153
1161
  tab.click(selector, button=, click_count=) → None (auto-waits 2s, mouse event)
1154
1162
  tab.tap(selector_or_x, y=) → None (touch event, 3s timeout)
1163
+ tab.front() → None (Page.bringToFront; click/tap/type_text/key auto-call this on a hidden tab)
1155
1164
  tab.swipe(x1, y1, x2, y2, steps=, duration_ms=) → None (touch scroll)
1156
1165
  tab.scroll(selector, dy=, dx=, steps=, duration_ms=) → None (touch-scroll container)
1157
1166
  tab.type_text(selector, text, delay_ms=, press_enter=) → None (clears first, auto-waits)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes