oagi-core 0.13.2__py3-none-any.whl → 0.14.1__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.
- oagi/__init__.py +4 -0
- oagi/agent/default.py +4 -6
- oagi/agent/tasker/taskee_agent.py +4 -6
- oagi/cli/agent.py +44 -0
- oagi/constants.py +1 -1
- oagi/handler/__init__.py +3 -0
- oagi/handler/async_pyautogui_action_handler.py +12 -1
- oagi/handler/async_screenshot_maker.py +5 -0
- oagi/handler/async_ydotool_action_handler.py +10 -0
- oagi/handler/pil_image.py +35 -4
- oagi/handler/pyautogui_action_handler.py +28 -0
- oagi/handler/screen_manager.py +187 -0
- oagi/handler/screenshot_maker.py +8 -1
- oagi/handler/utils.py +14 -0
- oagi/handler/wayland_support.py +6 -2
- oagi/handler/ydotool_action_handler.py +28 -0
- {oagi_core-0.13.2.dist-info → oagi_core-0.14.1.dist-info}/METADATA +72 -10
- {oagi_core-0.13.2.dist-info → oagi_core-0.14.1.dist-info}/RECORD +21 -20
- {oagi_core-0.13.2.dist-info → oagi_core-0.14.1.dist-info}/WHEEL +0 -0
- {oagi_core-0.13.2.dist-info → oagi_core-0.14.1.dist-info}/entry_points.txt +0 -0
- {oagi_core-0.13.2.dist-info → oagi_core-0.14.1.dist-info}/licenses/LICENSE +0 -0
oagi/__init__.py
CHANGED
|
@@ -81,6 +81,7 @@ _LAZY_IMPORTS_DATA: dict[str, tuple[str, str | None, str | None]] = {
|
|
|
81
81
|
"screeninfo",
|
|
82
82
|
"desktop",
|
|
83
83
|
),
|
|
84
|
+
"ScreenManager": ("oagi.handler.screen_manager", None, None),
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
if TYPE_CHECKING:
|
|
@@ -95,6 +96,7 @@ if TYPE_CHECKING:
|
|
|
95
96
|
PyautoguiActionHandler,
|
|
96
97
|
PyautoguiConfig,
|
|
97
98
|
)
|
|
99
|
+
from oagi.handler.screen_manager import ScreenManager
|
|
98
100
|
from oagi.handler.screenshot_maker import ScreenshotMaker
|
|
99
101
|
from oagi.handler.ydotool_action_handler import YdotoolActionHandler, YdotoolConfig
|
|
100
102
|
from oagi.server.config import ServerConfig
|
|
@@ -170,4 +172,6 @@ __all__ = [
|
|
|
170
172
|
"AsyncYdotoolActionHandler",
|
|
171
173
|
"YdotoolActionHandler",
|
|
172
174
|
"YdotoolConfig",
|
|
175
|
+
# Lazy imports - Screen manager
|
|
176
|
+
"ScreenManager",
|
|
173
177
|
]
|
oagi/agent/default.py
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
# Licensed under the MIT License.
|
|
7
7
|
# -----------------------------------------------------------------------------
|
|
8
8
|
|
|
9
|
-
import asyncio
|
|
10
9
|
import logging
|
|
11
10
|
|
|
12
11
|
from .. import AsyncActor
|
|
@@ -16,7 +15,7 @@ from ..constants import (
|
|
|
16
15
|
DEFAULT_TEMPERATURE,
|
|
17
16
|
MODEL_ACTOR,
|
|
18
17
|
)
|
|
19
|
-
from ..handler.utils import reset_handler
|
|
18
|
+
from ..handler.utils import configure_handler_delay, reset_handler
|
|
20
19
|
from ..types import (
|
|
21
20
|
ActionEvent,
|
|
22
21
|
AsyncActionHandler,
|
|
@@ -72,6 +71,9 @@ class AsyncDefaultAgent:
|
|
|
72
71
|
# Reset handler state at automation start
|
|
73
72
|
reset_handler(action_handler)
|
|
74
73
|
|
|
74
|
+
# Configure handler's post_batch_delay from agent's step_delay
|
|
75
|
+
configure_handler_delay(action_handler, self.step_delay)
|
|
76
|
+
|
|
75
77
|
for i in range(self.max_steps):
|
|
76
78
|
step_num = i + 1
|
|
77
79
|
logger.debug(f"Executing step {step_num}/{self.max_steps}")
|
|
@@ -127,10 +129,6 @@ class AsyncDefaultAgent:
|
|
|
127
129
|
)
|
|
128
130
|
)
|
|
129
131
|
|
|
130
|
-
# Wait after actions before next screenshot
|
|
131
|
-
if self.step_delay > 0:
|
|
132
|
-
await asyncio.sleep(self.step_delay)
|
|
133
|
-
|
|
134
132
|
# Check if task is complete
|
|
135
133
|
if step.stop:
|
|
136
134
|
logger.info(f"Task completed successfully after {step_num} steps")
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
# Licensed under the MIT License.
|
|
7
7
|
# -----------------------------------------------------------------------------
|
|
8
8
|
|
|
9
|
-
import asyncio
|
|
10
9
|
import logging
|
|
11
10
|
from datetime import datetime
|
|
12
11
|
from typing import Any
|
|
@@ -19,7 +18,7 @@ from oagi.constants import (
|
|
|
19
18
|
DEFAULT_TEMPERATURE,
|
|
20
19
|
MODEL_ACTOR,
|
|
21
20
|
)
|
|
22
|
-
from oagi.handler.utils import reset_handler
|
|
21
|
+
from oagi.handler.utils import configure_handler_delay, reset_handler
|
|
23
22
|
from oagi.types import (
|
|
24
23
|
URL,
|
|
25
24
|
ActionEvent,
|
|
@@ -126,6 +125,9 @@ class TaskeeAgent(AsyncAgent):
|
|
|
126
125
|
# Reset handler state at todo execution start
|
|
127
126
|
reset_handler(action_handler)
|
|
128
127
|
|
|
128
|
+
# Configure handler's post_batch_delay from agent's step_delay
|
|
129
|
+
configure_handler_delay(action_handler, self.step_delay)
|
|
130
|
+
|
|
129
131
|
self.current_todo = instruction
|
|
130
132
|
self.actions = []
|
|
131
133
|
self.total_actions = 0
|
|
@@ -355,10 +357,6 @@ class TaskeeAgent(AsyncAgent):
|
|
|
355
357
|
self.total_actions += len(step.actions)
|
|
356
358
|
self.since_reflection += len(step.actions)
|
|
357
359
|
|
|
358
|
-
# Wait after actions before next screenshot
|
|
359
|
-
if self.step_delay > 0:
|
|
360
|
-
await asyncio.sleep(self.step_delay)
|
|
361
|
-
|
|
362
360
|
steps_taken += 1
|
|
363
361
|
|
|
364
362
|
# Check if task is complete
|
oagi/cli/agent.py
CHANGED
|
@@ -86,6 +86,11 @@ def add_agent_parser(subparsers: argparse._SubParsersAction) -> None:
|
|
|
86
86
|
type=float,
|
|
87
87
|
help=f"Delay in seconds after each step before next screenshot (default: {DEFAULT_STEP_DELAY})",
|
|
88
88
|
)
|
|
89
|
+
run_parser.add_argument(
|
|
90
|
+
"--screen-index",
|
|
91
|
+
type=int,
|
|
92
|
+
help="Choose the index of screen to run the task",
|
|
93
|
+
)
|
|
89
94
|
|
|
90
95
|
# agent modes command
|
|
91
96
|
agent_subparsers.add_parser("modes", help="List available agent modes")
|
|
@@ -96,6 +101,11 @@ def add_agent_parser(subparsers: argparse._SubParsersAction) -> None:
|
|
|
96
101
|
help="Check macOS permissions for screen recording and accessibility",
|
|
97
102
|
)
|
|
98
103
|
|
|
104
|
+
# agent screens command
|
|
105
|
+
agent_subparsers.add_parser(
|
|
106
|
+
"screens", help="List all available screens for agent execution"
|
|
107
|
+
)
|
|
108
|
+
|
|
99
109
|
|
|
100
110
|
def handle_agent_command(args: argparse.Namespace) -> None:
|
|
101
111
|
if args.agent_command == "run":
|
|
@@ -104,6 +114,19 @@ def handle_agent_command(args: argparse.Namespace) -> None:
|
|
|
104
114
|
list_modes()
|
|
105
115
|
elif args.agent_command == "permission":
|
|
106
116
|
check_permissions()
|
|
117
|
+
elif args.agent_command == "screens":
|
|
118
|
+
list_screens()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def list_screens() -> None:
|
|
122
|
+
"""List all available screens for agent execution."""
|
|
123
|
+
from oagi import ScreenManager # noqa: PLC0415
|
|
124
|
+
|
|
125
|
+
screen_manager = ScreenManager()
|
|
126
|
+
screens = screen_manager.get_all_screens()
|
|
127
|
+
print("Available screens:")
|
|
128
|
+
for screen_index, screen in enumerate(screens):
|
|
129
|
+
print(f" - Index {screen_index}: {screen}")
|
|
107
130
|
|
|
108
131
|
|
|
109
132
|
def list_modes() -> None:
|
|
@@ -212,6 +235,22 @@ def run_agent(args: argparse.Namespace) -> None:
|
|
|
212
235
|
from oagi.agent import create_agent # noqa: PLC0415
|
|
213
236
|
from oagi.handler.wayland_support import is_wayland_display_server # noqa: PLC0415
|
|
214
237
|
|
|
238
|
+
# Create screen manager for multi-screen support
|
|
239
|
+
# Must be initialized before importing pyautogui to ensure correct DPI awareness in Windows
|
|
240
|
+
target_screen = None
|
|
241
|
+
if args.screen_index is not None:
|
|
242
|
+
from oagi.handler import ScreenManager # noqa: PLC0415
|
|
243
|
+
|
|
244
|
+
screen_index = args.screen_index
|
|
245
|
+
screen_manager = ScreenManager()
|
|
246
|
+
all_screens = screen_manager.get_all_screens()
|
|
247
|
+
if screen_index >= len(all_screens) or screen_index < 0:
|
|
248
|
+
raise ValueError(
|
|
249
|
+
f"Error: Screen index {screen_index} not found. Available screen indices: {list(range(len(all_screens)))}"
|
|
250
|
+
)
|
|
251
|
+
target_screen = all_screens[screen_index]
|
|
252
|
+
print(f"Target screen: {target_screen}")
|
|
253
|
+
|
|
215
254
|
# Select appropriate action handler based on display server
|
|
216
255
|
if is_wayland_display_server():
|
|
217
256
|
check_optional_dependency("screeninfo", "Agent execution (Wayland)", "desktop")
|
|
@@ -280,6 +319,11 @@ def run_agent(args: argparse.Namespace) -> None:
|
|
|
280
319
|
# Create image provider
|
|
281
320
|
image_provider = AsyncScreenshotMaker()
|
|
282
321
|
|
|
322
|
+
if target_screen:
|
|
323
|
+
# Set the target screen for the image and action provider
|
|
324
|
+
image_provider.set_target_screen(target_screen)
|
|
325
|
+
action_handler.set_target_screen(target_screen)
|
|
326
|
+
|
|
283
327
|
if args.instruction:
|
|
284
328
|
print(f"Starting agent with instruction: {args.instruction}")
|
|
285
329
|
else:
|
oagi/constants.py
CHANGED
oagi/handler/__init__.py
CHANGED
|
@@ -21,6 +21,7 @@ _LAZY_IMPORTS: dict[str, str] = {
|
|
|
21
21
|
"AsyncYdotoolActionHandler": "oagi.handler.async_ydotool_action_handler",
|
|
22
22
|
"YdotoolActionHandler": "oagi.handler.ydotool_action_handler",
|
|
23
23
|
"YdotoolConfig": "oagi.handler.ydotool_action_handler",
|
|
24
|
+
"ScreenManager": "oagi.handler.screen_manager",
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
if TYPE_CHECKING:
|
|
@@ -32,6 +33,7 @@ if TYPE_CHECKING:
|
|
|
32
33
|
PyautoguiActionHandler,
|
|
33
34
|
PyautoguiConfig,
|
|
34
35
|
)
|
|
36
|
+
from oagi.handler.screen_manager import ScreenManager
|
|
35
37
|
from oagi.handler.screenshot_maker import ScreenshotMaker
|
|
36
38
|
from oagi.handler.ydotool_action_handler import YdotoolActionHandler, YdotoolConfig
|
|
37
39
|
|
|
@@ -60,4 +62,5 @@ __all__ = [
|
|
|
60
62
|
"YdotoolConfig",
|
|
61
63
|
"YdotoolActionHandler",
|
|
62
64
|
"AsyncYdotoolActionHandler",
|
|
65
|
+
"ScreenManager",
|
|
63
66
|
]
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
import asyncio
|
|
10
10
|
|
|
11
|
+
from oagi.handler.screen_manager import Screen
|
|
12
|
+
|
|
11
13
|
from ..types import Action
|
|
12
14
|
from .pyautogui_action_handler import PyautoguiActionHandler, PyautoguiConfig
|
|
13
15
|
|
|
@@ -27,7 +29,16 @@ class AsyncPyautoguiActionHandler:
|
|
|
27
29
|
config: PyautoguiConfig instance for customizing behavior
|
|
28
30
|
"""
|
|
29
31
|
self.sync_handler = PyautoguiActionHandler(config=config)
|
|
30
|
-
|
|
32
|
+
# Share the same config object so configure_handler_delay() works
|
|
33
|
+
self.config = self.sync_handler.config
|
|
34
|
+
|
|
35
|
+
def set_target_screen(self, screen: Screen) -> None:
|
|
36
|
+
"""Set the target screen for the action handler.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
screen (Screen): The screen object to set as the target.
|
|
40
|
+
"""
|
|
41
|
+
self.sync_handler.set_target_screen(screen)
|
|
31
42
|
|
|
32
43
|
def reset(self):
|
|
33
44
|
"""Reset handler state.
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
import asyncio
|
|
10
10
|
|
|
11
|
+
from oagi.handler.screen_manager import Screen
|
|
12
|
+
|
|
11
13
|
from ..types import Image, ImageConfig
|
|
12
14
|
from .screenshot_maker import ScreenshotMaker
|
|
13
15
|
|
|
@@ -29,6 +31,9 @@ class AsyncScreenshotMaker:
|
|
|
29
31
|
self.sync_screenshot_maker = ScreenshotMaker(config=config)
|
|
30
32
|
self.config = config
|
|
31
33
|
|
|
34
|
+
def set_target_screen(self, screen: Screen) -> None:
|
|
35
|
+
self.sync_screenshot_maker.set_target_screen(screen)
|
|
36
|
+
|
|
32
37
|
async def __call__(self) -> Image:
|
|
33
38
|
"""
|
|
34
39
|
Capture a screenshot asynchronously using a thread pool executor.
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
import asyncio
|
|
10
10
|
|
|
11
|
+
from oagi.handler.screen_manager import Screen
|
|
12
|
+
|
|
11
13
|
from ..types import Action
|
|
12
14
|
from .ydotool_action_handler import YdotoolActionHandler, YdotoolConfig
|
|
13
15
|
|
|
@@ -29,6 +31,14 @@ class AsyncYdotoolActionHandler:
|
|
|
29
31
|
self.config = config or YdotoolConfig()
|
|
30
32
|
self.sync_handler = YdotoolActionHandler(config=self.config)
|
|
31
33
|
|
|
34
|
+
def set_target_screen(self, screen: Screen) -> None:
|
|
35
|
+
"""Set the target screen for the action handler.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
screen (Screen): The screen object to set as the target.
|
|
39
|
+
"""
|
|
40
|
+
self.sync_handler.set_target_screen(screen)
|
|
41
|
+
|
|
32
42
|
def reset(self):
|
|
33
43
|
"""Reset handler state.
|
|
34
44
|
|
oagi/handler/pil_image.py
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
# -----------------------------------------------------------------------------
|
|
8
8
|
|
|
9
9
|
import io
|
|
10
|
+
import sys
|
|
10
11
|
|
|
11
12
|
from ..exceptions import check_optional_dependency
|
|
12
13
|
from ..types.models.image_config import ImageConfig
|
|
@@ -39,17 +40,47 @@ class PILImage:
|
|
|
39
40
|
return cls(image, config)
|
|
40
41
|
|
|
41
42
|
@classmethod
|
|
42
|
-
def from_screenshot(
|
|
43
|
-
|
|
43
|
+
def from_screenshot(
|
|
44
|
+
cls,
|
|
45
|
+
config: ImageConfig | None = None,
|
|
46
|
+
region: tuple[int, int, int, int] | None = None,
|
|
47
|
+
) -> "PILImage":
|
|
48
|
+
"""Create PILImage from screenshot.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
config: ImageConfig for transformations
|
|
52
|
+
region: Optional (x, y, width, height) tuple for cropping
|
|
53
|
+
"""
|
|
44
54
|
# Use flameshot by default in Wayland display environment
|
|
45
55
|
if is_wayland_display_server():
|
|
46
|
-
return cls(wayland_screenshot(), config)
|
|
56
|
+
return cls(wayland_screenshot(region=region), config)
|
|
47
57
|
|
|
48
58
|
# Lazy import to avoid DISPLAY issues in headless environments
|
|
49
59
|
check_optional_dependency("pyautogui", "PILImage.from_screenshot()", "desktop")
|
|
50
60
|
import pyautogui # noqa: PLC0415
|
|
51
61
|
|
|
52
|
-
|
|
62
|
+
if sys.platform == "win32" and region is not None:
|
|
63
|
+
# Use mss instead of pyautogui for screenshots in multi-monitor Windows setups
|
|
64
|
+
import mss # noqa: PLC0415
|
|
65
|
+
|
|
66
|
+
with mss.mss() as sct:
|
|
67
|
+
screenshot_data = sct.grab(
|
|
68
|
+
{
|
|
69
|
+
"top": region[1],
|
|
70
|
+
"left": region[0],
|
|
71
|
+
"width": region[2],
|
|
72
|
+
"height": region[3],
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
screenshot = PILImageLib.frombytes(
|
|
76
|
+
"RGB",
|
|
77
|
+
screenshot_data.size,
|
|
78
|
+
screenshot_data.bgra,
|
|
79
|
+
"raw",
|
|
80
|
+
"BGRX",
|
|
81
|
+
)
|
|
82
|
+
else:
|
|
83
|
+
screenshot = pyautogui.screenshot(region=region)
|
|
53
84
|
return cls(screenshot, config)
|
|
54
85
|
|
|
55
86
|
def transform(self, config: ImageConfig) -> "PILImage":
|
|
@@ -11,6 +11,9 @@ import time
|
|
|
11
11
|
|
|
12
12
|
from pydantic import BaseModel, Field
|
|
13
13
|
|
|
14
|
+
from oagi.handler.screen_manager import Screen
|
|
15
|
+
|
|
16
|
+
from ..constants import DEFAULT_STEP_DELAY
|
|
14
17
|
from ..exceptions import check_optional_dependency
|
|
15
18
|
from ..types import Action, ActionType, parse_coords, parse_drag_coords, parse_scroll
|
|
16
19
|
from .capslock_manager import CapsLockManager
|
|
@@ -55,6 +58,12 @@ class PyautoguiConfig(BaseModel):
|
|
|
55
58
|
default=0.1,
|
|
56
59
|
description="Delay in seconds after moving to position before clicking",
|
|
57
60
|
)
|
|
61
|
+
post_batch_delay: float = Field(
|
|
62
|
+
default=DEFAULT_STEP_DELAY,
|
|
63
|
+
ge=0,
|
|
64
|
+
description="Delay after executing all actions in a batch (seconds). "
|
|
65
|
+
"Allows UI to settle before next screenshot.",
|
|
66
|
+
)
|
|
58
67
|
|
|
59
68
|
|
|
60
69
|
class PyautoguiActionHandler:
|
|
@@ -81,6 +90,8 @@ class PyautoguiActionHandler:
|
|
|
81
90
|
pyautogui.PAUSE = self.config.action_pause
|
|
82
91
|
# Initialize caps lock manager
|
|
83
92
|
self.caps_manager = CapsLockManager(mode=self.config.capslock_mode)
|
|
93
|
+
# The origin position of coordinates (the top-left corner of the target screen)
|
|
94
|
+
self.origin_x, self.origin_y = 0, 0
|
|
84
95
|
|
|
85
96
|
def reset(self):
|
|
86
97
|
"""Reset handler state.
|
|
@@ -90,6 +101,15 @@ class PyautoguiActionHandler:
|
|
|
90
101
|
"""
|
|
91
102
|
self.caps_manager.reset()
|
|
92
103
|
|
|
104
|
+
def set_target_screen(self, screen: Screen) -> None:
|
|
105
|
+
"""Set the target screen for the action handler.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
screen (Screen): The screen object to set as the target.
|
|
109
|
+
"""
|
|
110
|
+
self.screen_width, self.screen_height = screen.width, screen.height
|
|
111
|
+
self.origin_x, self.origin_y = screen.x, screen.y
|
|
112
|
+
|
|
93
113
|
def _denormalize_coords(self, x: float, y: float) -> tuple[int, int]:
|
|
94
114
|
"""Convert coordinates from 0-1000 range to actual screen coordinates.
|
|
95
115
|
|
|
@@ -111,6 +131,10 @@ class PyautoguiActionHandler:
|
|
|
111
131
|
elif screen_y > self.screen_height - 1:
|
|
112
132
|
screen_y = self.screen_height - 1
|
|
113
133
|
|
|
134
|
+
# Add origin offset to convert relative to top-left corner
|
|
135
|
+
screen_x += self.origin_x
|
|
136
|
+
screen_y += self.origin_y
|
|
137
|
+
|
|
114
138
|
return screen_x, screen_y
|
|
115
139
|
|
|
116
140
|
def _parse_coords(self, args_str: str) -> tuple[int, int]:
|
|
@@ -275,3 +299,7 @@ class PyautoguiActionHandler:
|
|
|
275
299
|
except Exception as e:
|
|
276
300
|
print(f"Error executing action {action.type}: {e}")
|
|
277
301
|
raise
|
|
302
|
+
|
|
303
|
+
# Wait after batch for UI to settle before next screenshot
|
|
304
|
+
if self.config.post_batch_delay > 0:
|
|
305
|
+
time.sleep(self.config.post_batch_delay)
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# -----------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) OpenAGI Foundation
|
|
3
|
+
# All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# This file is part of the official API project.
|
|
6
|
+
# Licensed under the MIT License.
|
|
7
|
+
# -----------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
import sys
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
|
|
13
|
+
from oagi.exceptions import check_optional_dependency
|
|
14
|
+
|
|
15
|
+
# Guard flag to prevent multiple DPI awareness calls on Windows
|
|
16
|
+
_dpi_awareness_set = False
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class Screen:
|
|
21
|
+
"""
|
|
22
|
+
Screen represents a single display screen.
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
name (str): The name of the screen.
|
|
26
|
+
x (int): The x-coordinate of the top-left corner of the screen.
|
|
27
|
+
y (int): The y-coordinate of the top-left corner of the screen.
|
|
28
|
+
width (int): The width of the screen in pixels.
|
|
29
|
+
height (int): The height of the screen in pixels.
|
|
30
|
+
is_primary (bool): True if this is the primary screen, False otherwise.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
name: str
|
|
34
|
+
x: int
|
|
35
|
+
y: int
|
|
36
|
+
width: int
|
|
37
|
+
height: int
|
|
38
|
+
is_primary: bool = False
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ScreenManager:
|
|
42
|
+
"""
|
|
43
|
+
ScreenManager is responsible for detecting and managing screens.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self):
|
|
47
|
+
self.screens = []
|
|
48
|
+
# Enable DPI awareness if on Windows
|
|
49
|
+
if sys.platform == "win32":
|
|
50
|
+
self.enable_windows_dpi_awareness()
|
|
51
|
+
|
|
52
|
+
def get_all_screens(self) -> list[Screen]:
|
|
53
|
+
if self.screens:
|
|
54
|
+
return self.screens
|
|
55
|
+
if sys.platform == "darwin":
|
|
56
|
+
screens = self._get_darwin_screen_info()
|
|
57
|
+
elif sys.platform == "win32":
|
|
58
|
+
screens = self._get_windows_screen_info()
|
|
59
|
+
else:
|
|
60
|
+
screens = self._get_linux_screen_info()
|
|
61
|
+
# Find the primary screen
|
|
62
|
+
primary_screen, alternative_screens = None, []
|
|
63
|
+
for screen in screens:
|
|
64
|
+
if screen.is_primary:
|
|
65
|
+
primary_screen = screen
|
|
66
|
+
else:
|
|
67
|
+
alternative_screens.append(screen)
|
|
68
|
+
# order the alternative_screens by x coordinate ascending and y coordinate ascending
|
|
69
|
+
alternative_screens = sorted(
|
|
70
|
+
alternative_screens, key=lambda item: (item.x, item.y)
|
|
71
|
+
)
|
|
72
|
+
# Add the primary screen to the front of the list if it exists
|
|
73
|
+
if primary_screen:
|
|
74
|
+
self.screens = [primary_screen]
|
|
75
|
+
self.screens += alternative_screens
|
|
76
|
+
return self.screens
|
|
77
|
+
|
|
78
|
+
def _get_darwin_screen_info(self) -> list[Screen]:
|
|
79
|
+
"""
|
|
80
|
+
Get screen information for macOS using AppKit.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
list[Screen]: A list of Screen objects representing all detected screens.
|
|
84
|
+
"""
|
|
85
|
+
check_optional_dependency("AppKit", "ScreenManager", "desktop")
|
|
86
|
+
import AppKit # noqa: PLC0415
|
|
87
|
+
|
|
88
|
+
# Force the RunLoop to update once
|
|
89
|
+
# This "accepts input" which forces macOS to update screen geometry
|
|
90
|
+
loop = AppKit.NSRunLoop.currentRunLoop()
|
|
91
|
+
loop.acceptInputForMode_beforeDate_(
|
|
92
|
+
AppKit.NSDefaultRunLoopMode, AppKit.NSDate.distantPast()
|
|
93
|
+
)
|
|
94
|
+
# Retrieve screen information using AppKit
|
|
95
|
+
screens = AppKit.NSScreen.screens()
|
|
96
|
+
screen_list = []
|
|
97
|
+
for screen in screens:
|
|
98
|
+
frame = screen.frame()
|
|
99
|
+
# Origin (0,0) is bottom-left of the primary screen
|
|
100
|
+
x, y = int(frame.origin.x), int(frame.origin.y)
|
|
101
|
+
width, height = int(frame.size.width), int(frame.size.height)
|
|
102
|
+
name = screen.localizedName()
|
|
103
|
+
# Normalize the origin to Top-Left
|
|
104
|
+
y = int(AppKit.NSScreen.screens()[0].frame().size.height) - (y + height)
|
|
105
|
+
screen_list.append(Screen(name, x, y, width, height, x == 0 and y == 0))
|
|
106
|
+
return screen_list
|
|
107
|
+
|
|
108
|
+
def _get_windows_screen_info(self) -> list[Screen]:
|
|
109
|
+
"""
|
|
110
|
+
Get screen information for Windows using mss.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
list[Screen]: A list of Screen objects representing all detected screens.
|
|
114
|
+
"""
|
|
115
|
+
check_optional_dependency("mss", "ScreenManager", "desktop")
|
|
116
|
+
import mss # noqa: PLC0415
|
|
117
|
+
|
|
118
|
+
screen_list = []
|
|
119
|
+
for index, screen in enumerate(mss.mss().monitors[1:]):
|
|
120
|
+
screen_list.append(
|
|
121
|
+
Screen(
|
|
122
|
+
f"DISPLAY{index}",
|
|
123
|
+
screen["left"],
|
|
124
|
+
screen["top"],
|
|
125
|
+
screen["width"],
|
|
126
|
+
screen["height"],
|
|
127
|
+
screen["top"] == 0 and screen["left"] == 0,
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
return screen_list
|
|
131
|
+
|
|
132
|
+
def _get_linux_screen_info(self) -> list[Screen]:
|
|
133
|
+
"""
|
|
134
|
+
Get screen information for Linux and other platforms as default.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
list[Screen]: A list of Screen objects representing all detected screens.
|
|
138
|
+
"""
|
|
139
|
+
check_optional_dependency("screeninfo", "ScreenManager", "desktop")
|
|
140
|
+
import screeninfo # noqa: PLC0415
|
|
141
|
+
|
|
142
|
+
screen_list = []
|
|
143
|
+
for screen in screeninfo.get_monitors():
|
|
144
|
+
screen_list.append(
|
|
145
|
+
Screen(
|
|
146
|
+
screen.name,
|
|
147
|
+
screen.x,
|
|
148
|
+
screen.y,
|
|
149
|
+
screen.width,
|
|
150
|
+
screen.height,
|
|
151
|
+
screen.is_primary,
|
|
152
|
+
)
|
|
153
|
+
)
|
|
154
|
+
return screen_list
|
|
155
|
+
|
|
156
|
+
@staticmethod
|
|
157
|
+
def enable_windows_dpi_awareness():
|
|
158
|
+
"""
|
|
159
|
+
Enable per-monitor DPI awareness to fix multi-monitor scaling issues.
|
|
160
|
+
|
|
161
|
+
On Windows with mixed scaling between monitors, applications that are not
|
|
162
|
+
DPI-aware will have their coordinates virtualized, causing clicks/moves to
|
|
163
|
+
land at incorrect positions. Enabling DPI awareness ensures PyAutoGUI and mss
|
|
164
|
+
works in physical pixels across all monitors.
|
|
165
|
+
|
|
166
|
+
This method is idempotent - subsequent calls after the first successful call
|
|
167
|
+
will be no-ops.
|
|
168
|
+
"""
|
|
169
|
+
global _dpi_awareness_set
|
|
170
|
+
if _dpi_awareness_set:
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
import ctypes # noqa: PLC0415
|
|
174
|
+
|
|
175
|
+
try:
|
|
176
|
+
# For Windows 8.1 and Windows 10/11
|
|
177
|
+
# 2 = PROCESS_PER_MONITOR_DPI_AWARE
|
|
178
|
+
PROCESS_PER_MONITOR_DPI_AWARE = 2
|
|
179
|
+
ctypes.windll.shcore.SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE)
|
|
180
|
+
_dpi_awareness_set = True
|
|
181
|
+
except Exception:
|
|
182
|
+
try:
|
|
183
|
+
# Fallback for older Windows versions
|
|
184
|
+
ctypes.windll.user32.SetProcessDPIAware()
|
|
185
|
+
_dpi_awareness_set = True
|
|
186
|
+
except Exception:
|
|
187
|
+
raise RuntimeError("Could not set DPI awareness")
|
oagi/handler/screenshot_maker.py
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
from typing import Optional
|
|
10
10
|
|
|
11
|
+
from oagi.handler.screen_manager import Screen
|
|
12
|
+
|
|
11
13
|
from ..types import Image
|
|
12
14
|
from ..types.models.image_config import ImageConfig
|
|
13
15
|
from .pil_image import PILImage
|
|
@@ -19,11 +21,16 @@ class ScreenshotMaker:
|
|
|
19
21
|
def __init__(self, config: ImageConfig | None = None):
|
|
20
22
|
self.config = config or ImageConfig()
|
|
21
23
|
self._last_image: Optional[PILImage] = None
|
|
24
|
+
self.region: Optional[tuple[int, int, int, int]] = None
|
|
25
|
+
|
|
26
|
+
def set_target_screen(self, screen: Screen) -> None:
|
|
27
|
+
"""Set the target screen for screenshotting."""
|
|
28
|
+
self.region = (screen.x, screen.y, screen.width, screen.height)
|
|
22
29
|
|
|
23
30
|
def __call__(self) -> Image:
|
|
24
31
|
"""Take and process a screenshot."""
|
|
25
32
|
# Create PILImage from screenshot
|
|
26
|
-
pil_image = PILImage.from_screenshot()
|
|
33
|
+
pil_image = PILImage.from_screenshot(region=self.region)
|
|
27
34
|
|
|
28
35
|
# Apply transformation if config is set
|
|
29
36
|
if self.config:
|
oagi/handler/utils.py
CHANGED
|
@@ -19,3 +19,17 @@ def reset_handler(handler) -> None:
|
|
|
19
19
|
"""
|
|
20
20
|
if hasattr(handler, "reset"):
|
|
21
21
|
handler.reset()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def configure_handler_delay(handler, step_delay: float) -> None:
|
|
25
|
+
"""Configure handler's post_batch_delay from agent's step_delay.
|
|
26
|
+
|
|
27
|
+
Uses duck-typing to check if the handler has a config with post_batch_delay.
|
|
28
|
+
This allows agents to control the delay after action execution.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
handler: The action handler to configure
|
|
32
|
+
step_delay: The delay in seconds to set
|
|
33
|
+
"""
|
|
34
|
+
if hasattr(handler, "config") and hasattr(handler.config, "post_batch_delay"):
|
|
35
|
+
handler.config.post_batch_delay = step_delay
|
oagi/handler/wayland_support.py
CHANGED
|
@@ -43,7 +43,7 @@ def get_screen_size() -> tuple[int, int]:
|
|
|
43
43
|
raise Exception("No monitor found, cannot get the screen size info")
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
def screenshot() -> Image:
|
|
46
|
+
def screenshot(region: tuple[int, int, int, int] | None = None) -> Image:
|
|
47
47
|
"""
|
|
48
48
|
Use Flameshot to take a screenshot and return an Image object
|
|
49
49
|
|
|
@@ -52,7 +52,11 @@ def screenshot() -> Image:
|
|
|
52
52
|
# Check if flameshot is installed
|
|
53
53
|
if shutil.which("flameshot") is None:
|
|
54
54
|
raise RuntimeError("flameshot not found. Ensure it is installed and in PATH.")
|
|
55
|
-
cmd = ["flameshot", "full", "--
|
|
55
|
+
cmd = ["flameshot", "full", "--raw"]
|
|
56
|
+
if region:
|
|
57
|
+
cmd.extend(["--region", f"{region[2]}x{region[3]}+{region[0]}+{region[1]}"])
|
|
58
|
+
else:
|
|
59
|
+
cmd.extend(["--region", "all"])
|
|
56
60
|
res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
57
61
|
if res.returncode != 0:
|
|
58
62
|
raise RuntimeError(
|
|
@@ -10,6 +10,9 @@ import time
|
|
|
10
10
|
|
|
11
11
|
from pydantic import BaseModel, Field
|
|
12
12
|
|
|
13
|
+
from oagi.handler.screen_manager import Screen
|
|
14
|
+
|
|
15
|
+
from ..constants import DEFAULT_STEP_DELAY
|
|
13
16
|
from ..types import Action, ActionType, parse_coords, parse_drag_coords, parse_scroll
|
|
14
17
|
from .capslock_manager import CapsLockManager
|
|
15
18
|
from .wayland_support import Ydotool, get_screen_size
|
|
@@ -36,6 +39,12 @@ class YdotoolConfig(BaseModel):
|
|
|
36
39
|
default="",
|
|
37
40
|
description="Custom socket address for ydotool (e.g., '/tmp/ydotool.sock')",
|
|
38
41
|
)
|
|
42
|
+
post_batch_delay: float = Field(
|
|
43
|
+
default=DEFAULT_STEP_DELAY,
|
|
44
|
+
ge=0,
|
|
45
|
+
description="Delay after executing all actions in a batch (seconds). "
|
|
46
|
+
"Allows UI to settle before next screenshot.",
|
|
47
|
+
)
|
|
39
48
|
|
|
40
49
|
|
|
41
50
|
class YdotoolActionHandler(Ydotool):
|
|
@@ -62,6 +71,8 @@ class YdotoolActionHandler(Ydotool):
|
|
|
62
71
|
self.action_pause = self.config.action_pause
|
|
63
72
|
# Initialize caps lock manager
|
|
64
73
|
self.caps_manager = CapsLockManager(mode=self.config.capslock_mode)
|
|
74
|
+
# The origin position of coordinates (the top-left corner of the screen)
|
|
75
|
+
self.origin_x, self.origin_y = 0, 0
|
|
65
76
|
|
|
66
77
|
def reset(self):
|
|
67
78
|
"""Reset handler state.
|
|
@@ -71,6 +82,15 @@ class YdotoolActionHandler(Ydotool):
|
|
|
71
82
|
"""
|
|
72
83
|
self.caps_manager.reset()
|
|
73
84
|
|
|
85
|
+
def set_target_screen(self, screen: Screen) -> None:
|
|
86
|
+
"""Set the target screen for the action handler.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
screen (Screen): The screen object to set as the target.
|
|
90
|
+
"""
|
|
91
|
+
self.screen_width, self.screen_height = screen.width, screen.height
|
|
92
|
+
self.origin_x, self.origin_y = screen.x, screen.y
|
|
93
|
+
|
|
74
94
|
def _execute_action(self, action: Action) -> bool:
|
|
75
95
|
"""
|
|
76
96
|
Execute a group of actions and return whether FINISH is reached.
|
|
@@ -168,6 +188,10 @@ class YdotoolActionHandler(Ydotool):
|
|
|
168
188
|
elif screen_y > self.screen_height - 1:
|
|
169
189
|
screen_y = self.screen_height - 1
|
|
170
190
|
|
|
191
|
+
# Add origin offset to convert relative to top-left corner
|
|
192
|
+
screen_x += self.origin_x
|
|
193
|
+
screen_y += self.origin_y
|
|
194
|
+
|
|
171
195
|
return screen_x, screen_y
|
|
172
196
|
|
|
173
197
|
def _normalize_key(self, key: str) -> str:
|
|
@@ -224,3 +248,7 @@ class YdotoolActionHandler(Ydotool):
|
|
|
224
248
|
except Exception as e:
|
|
225
249
|
print(f"Error executing action {action.type}: {e}")
|
|
226
250
|
raise
|
|
251
|
+
|
|
252
|
+
# Wait after batch for UI to settle before next screenshot
|
|
253
|
+
if self.config.post_batch_delay > 0:
|
|
254
|
+
time.sleep(self.config.post_batch_delay)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: oagi-core
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.1
|
|
4
4
|
Summary: Official API of OpenAGI Foundation
|
|
5
5
|
Project-URL: Homepage, https://github.com/agiopen-org/oagi
|
|
6
6
|
Author-email: OpenAGI Foundation <contact@agiopen.org>
|
|
@@ -31,6 +31,7 @@ Requires-Dist: openai>=1.3.0
|
|
|
31
31
|
Requires-Dist: pydantic>=2.0.0
|
|
32
32
|
Requires-Dist: rich>=10.0.0
|
|
33
33
|
Provides-Extra: desktop
|
|
34
|
+
Requires-Dist: mss>=9.0.0; (sys_platform == 'win32') and extra == 'desktop'
|
|
34
35
|
Requires-Dist: pillow>=9.0.0; extra == 'desktop'
|
|
35
36
|
Requires-Dist: pyautogui>=0.9.54; extra == 'desktop'
|
|
36
37
|
Requires-Dist: pyobjc-framework-applicationservices>=8.0; (sys_platform == 'darwin') and extra == 'desktop'
|
|
@@ -153,12 +154,15 @@ from oagi import AsyncPyautoguiActionHandler, PyautoguiConfig
|
|
|
153
154
|
|
|
154
155
|
# Customize action behavior
|
|
155
156
|
config = PyautoguiConfig(
|
|
156
|
-
drag_duration=1.0,
|
|
157
|
-
scroll_amount=50,
|
|
158
|
-
wait_duration=2.0,
|
|
159
|
-
action_pause=0.2,
|
|
160
|
-
hotkey_interval=0.1,
|
|
161
|
-
capslock_mode="session" # Caps lock mode: 'session' or 'system' (default: 'session')
|
|
157
|
+
drag_duration=1.0, # Slower drags for precision (default: 0.5)
|
|
158
|
+
scroll_amount=50, # Larger scroll steps (default: 2 on macOS, 100 on others)
|
|
159
|
+
wait_duration=2.0, # Longer waits for WAIT action (default: 1.0)
|
|
160
|
+
action_pause=0.2, # Pause between PyAutoGUI calls (default: 0.1)
|
|
161
|
+
hotkey_interval=0.1, # Interval between keys in hotkey combos (default: 0.1)
|
|
162
|
+
capslock_mode="session", # Caps lock mode: 'session' or 'system' (default: 'session')
|
|
163
|
+
macos_ctrl_to_cmd=True, # Replace ctrl with cmd on macOS (default: True)
|
|
164
|
+
click_pre_delay=0.1, # Delay after move before click (default: 0.1)
|
|
165
|
+
post_batch_delay=1.0, # Delay after actions before next screenshot (default: 1.0)
|
|
162
166
|
)
|
|
163
167
|
|
|
164
168
|
action_handler = AsyncPyautoguiActionHandler(config=config)
|
|
@@ -184,8 +188,14 @@ oagi agent modes
|
|
|
184
188
|
# Check macOS permissions (screen recording & accessibility)
|
|
185
189
|
oagi agent permission
|
|
186
190
|
|
|
191
|
+
# Print all available screens and their indices
|
|
192
|
+
oagi agent screens
|
|
193
|
+
|
|
187
194
|
# Export execution history
|
|
188
195
|
oagi agent run "Complete the form" --export html --export-file report.html
|
|
196
|
+
|
|
197
|
+
# Run with a specific screen
|
|
198
|
+
oagi agent run "Search weather on Google" --screen-index 1
|
|
189
199
|
```
|
|
190
200
|
|
|
191
201
|
CLI options:
|
|
@@ -196,6 +206,7 @@ CLI options:
|
|
|
196
206
|
- `--step-delay`: Delay after each action before next screenshot (default: 0.3s)
|
|
197
207
|
- `--export`: Export format (markdown, html, json)
|
|
198
208
|
- `--export-file`: Output file path for export
|
|
209
|
+
- `--screen-index`: Screen index for multi-screen environments
|
|
199
210
|
|
|
200
211
|
### Image Processing
|
|
201
212
|
|
|
@@ -271,20 +282,71 @@ from oagi import AsyncYdotoolActionHandler, YdotoolConfig
|
|
|
271
282
|
# Customize action behavior
|
|
272
283
|
config = YdotoolConfig(
|
|
273
284
|
scroll_amount=50, # Larger scroll steps (default: 20)
|
|
274
|
-
wait_duration=2.0, # Longer waits (default: 1.0)
|
|
275
|
-
action_pause=1.0, #
|
|
285
|
+
wait_duration=2.0, # Longer waits for WAIT action (default: 1.0)
|
|
286
|
+
action_pause=1.0, # Pause between Ydotool calls (default: 0.5)
|
|
276
287
|
capslock_mode="session", # Caps lock mode: 'session' or 'system' (default: 'session')
|
|
277
|
-
socket_address="/tmp/ydotool.sock" #
|
|
288
|
+
socket_address="/tmp/ydotool.sock", # Custom socket address (default: YDOTOOL_SOCKET env var)
|
|
289
|
+
post_batch_delay=1.0, # Delay after actions before next screenshot (default: 1.0)
|
|
278
290
|
)
|
|
279
291
|
|
|
280
292
|
action_handler = AsyncYdotoolActionHandler(config=config)
|
|
281
293
|
```
|
|
282
294
|
|
|
295
|
+
### Multi-Screen Execution
|
|
296
|
+
When running on multi-screen environments, you can choose which screen to use for task execution. The `ScreenManager` class provides methods to list available screens, while the `AsyncPyautoguiActionHandler` and `AsyncScreenshotMaker` classes allow you to set the target screen for actions and screenshots. In the result of `get_all_screens`, the primary screen is always the first one in the list and the remaining screens are appended in the ascending order of their origin coordinates.
|
|
297
|
+
|
|
298
|
+
```python
|
|
299
|
+
import asyncio
|
|
300
|
+
import sys
|
|
301
|
+
from oagi import ScreenManager
|
|
302
|
+
|
|
303
|
+
# Must be initialized before importing pyautogui to ensure correct DPI awareness in Windows
|
|
304
|
+
if sys.platform == "win32":
|
|
305
|
+
ScreenManager.enable_windows_dpi_awareness()
|
|
306
|
+
|
|
307
|
+
from oagi import (
|
|
308
|
+
AsyncDefaultAgent,
|
|
309
|
+
AsyncPyautoguiActionHandler,
|
|
310
|
+
AsyncScreenshotMaker,
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
def print_all_screens():
|
|
314
|
+
"""Print all available screens."""
|
|
315
|
+
screen_manager = ScreenManager()
|
|
316
|
+
all_screens = screen_manager.get_all_screens()
|
|
317
|
+
print("Available screens:")
|
|
318
|
+
for screen_index, screen in enumerate(all_screens):
|
|
319
|
+
print(f" - Index {screen_index}: {screen}")
|
|
320
|
+
|
|
321
|
+
async def main():
|
|
322
|
+
agent = AsyncDefaultAgent(max_steps=10)
|
|
323
|
+
action_handler = AsyncPyautoguiActionHandler()
|
|
324
|
+
image_provider = AsyncScreenshotMaker()
|
|
325
|
+
# Get all available screens
|
|
326
|
+
screen_manager = ScreenManager()
|
|
327
|
+
all_screens = screen_manager.get_all_screens()
|
|
328
|
+
# Choose a screen for task execution
|
|
329
|
+
screen_index = 1 # Use the second screen as example
|
|
330
|
+
target_screen = all_screens[screen_index]
|
|
331
|
+
# Set the target screen for handlers
|
|
332
|
+
action_handler.set_target_screen(target_screen)
|
|
333
|
+
image_provider.set_target_screen(target_screen)
|
|
334
|
+
completed = await agent.execute(
|
|
335
|
+
"Search weather on Google",
|
|
336
|
+
action_handler=action_handler,
|
|
337
|
+
image_provider=image_provider,
|
|
338
|
+
)
|
|
339
|
+
return completed
|
|
340
|
+
|
|
341
|
+
asyncio.run(main())
|
|
342
|
+
```
|
|
343
|
+
|
|
283
344
|
## Examples
|
|
284
345
|
|
|
285
346
|
See the [`examples/`](examples/) directory for more usage patterns:
|
|
286
347
|
- `execute_task_auto.py` - Automated task execution with `AsyncDefaultAgent`
|
|
287
348
|
- `execute_task_manual.py` - Manual step-by-step control with `Actor`
|
|
349
|
+
- `multi_screen_execution.py` - Automated task execution on multi-screen environments
|
|
288
350
|
- `continued_session.py` - Continuing tasks across sessions
|
|
289
351
|
- `screenshot_with_config.py` - Image compression and optimization
|
|
290
352
|
- `socketio_server_basic.py` - Socket.IO server example
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
oagi/__init__.py,sha256=
|
|
2
|
-
oagi/constants.py,sha256=
|
|
1
|
+
oagi/__init__.py,sha256=TVBbIMEXQh5Z8JumqrivMPBLYt3E5HmGPi973NDx2Qs,5797
|
|
2
|
+
oagi/constants.py,sha256=qcNisliLRZUr6krXpS49AAPfFYvkZuWQcAJ8ZdEnhic,1228
|
|
3
3
|
oagi/exceptions.py,sha256=Rco37GQTPYUfc2vRO3hozxPF_s8mKFDpFvBg2UKWo3Y,3066
|
|
4
4
|
oagi/logging.py,sha256=YT3KCMFj5fzO98R9xlDDgfSotUuz1xRD6OZeYM2rKoo,1760
|
|
5
5
|
oagi/platform_info.py,sha256=GEqNWnwePszVEM21toGGi07o3PaX8O059CYRr0RUM_M,1424
|
|
@@ -10,7 +10,7 @@ oagi/actor/base.py,sha256=-ecocyZ9-NQ4C4jjsHLUcs3ixZ99rFbzqwFsiVKZVR0,8001
|
|
|
10
10
|
oagi/actor/short.py,sha256=wKLCxvf7Ys6rYxXpHe4zbZdbf_1q1qcmm5WyWubwj3E,2630
|
|
11
11
|
oagi/actor/sync.py,sha256=QTY1WNTI75jwkWBghdVViHIp5rYkbm3kumlLedU8YeQ,3588
|
|
12
12
|
oagi/agent/__init__.py,sha256=KTVLUMhbjgpTJoOWMUZkkiqwhgumvbOZV2tJ9XCLfao,901
|
|
13
|
-
oagi/agent/default.py,sha256=
|
|
13
|
+
oagi/agent/default.py,sha256=mP4Gdld5I0sP0ANyn1CLkFvgk9qPkzYbwJeZUGhlsew,4712
|
|
14
14
|
oagi/agent/factories.py,sha256=syi_EOlU4SUjo-0CKaML8eIPu3ToUEKua2VHp9lvNF0,5839
|
|
15
15
|
oagi/agent/protocol.py,sha256=IQJGiMN4yZIacrh5e9JQsoM9TyHb8wJRQR4LAk8dSA0,1615
|
|
16
16
|
oagi/agent/registry.py,sha256=7bMA2-pH3xQ9ZavrHB_mnc2fOGSMeICPbOGtHoM7It0,4851
|
|
@@ -24,10 +24,10 @@ oagi/agent/tasker/__init__.py,sha256=1iTEFe7lzcqh96TL9R0QADPpLJLrUP0shtZ4DlZSv_8
|
|
|
24
24
|
oagi/agent/tasker/memory.py,sha256=NR13l5yxRA8GUE-oupAP4W1n80ZNG0SxpUfxsNltkUY,5033
|
|
25
25
|
oagi/agent/tasker/models.py,sha256=sMQgwIMKhT1tvVF2yoc1hh8GwEiJ6i6qPMy9WoiA8JM,2137
|
|
26
26
|
oagi/agent/tasker/planner.py,sha256=q6IvH6sfU2kYX1NcC9VHjGaQ0X9jF18yjuAYXisNCg0,15489
|
|
27
|
-
oagi/agent/tasker/taskee_agent.py,sha256=
|
|
27
|
+
oagi/agent/tasker/taskee_agent.py,sha256=IfRg_YixHkxHuiGCqok5cPDLD7PXUqlsoRAfWnWswMI,18091
|
|
28
28
|
oagi/agent/tasker/tasker_agent.py,sha256=yb0BdQzJyAPpK3njHPWgQruV8zpUGBXn1WjOGEMIO-g,11291
|
|
29
29
|
oagi/cli/__init__.py,sha256=aDnJViTseShpo5fdGPTj-ELysZhmdvB6Z8mEj2D-_N4,359
|
|
30
|
-
oagi/cli/agent.py,sha256=
|
|
30
|
+
oagi/cli/agent.py,sha256=CWvwwbo4eiq-USYmDHnKafEX8Nk6zsGsUmNzB4QftkQ,13186
|
|
31
31
|
oagi/cli/display.py,sha256=Y8_Dn5RIEfRqZUHVGF6URItW0C3XC7bPLWoAmmhvBS0,1829
|
|
32
32
|
oagi/cli/main.py,sha256=faHns0HaQCGyylDn2YZLpjQESuEiMYjoQVoMkt8FsH4,2292
|
|
33
33
|
oagi/cli/server.py,sha256=JFpzCOeaftITxesz8Ya-_Efs03bgotBg7aYwmMZhPwU,3033
|
|
@@ -37,20 +37,21 @@ oagi/client/__init__.py,sha256=F9DShPUdb6vZYmN1fpM1VYzp4MWqUao_e_R1KYmM4Q4,410
|
|
|
37
37
|
oagi/client/async_.py,sha256=BANE0KU14WBuXp6suBhr8JSlpWhN5SR2aJJ7wAJBDLQ,9574
|
|
38
38
|
oagi/client/base.py,sha256=CWAvE0AcpL8HD_i00n7Fq53AIAQGhBhS_n6LifUCqxE,14736
|
|
39
39
|
oagi/client/sync.py,sha256=4xNqqNihXmgLU385h22mMJ9wmmlw-jeOdWI4fmpEpTk,9369
|
|
40
|
-
oagi/handler/__init__.py,sha256=
|
|
40
|
+
oagi/handler/__init__.py,sha256=ZMQIeN_uJKUK_dn0w7ggsPfdRzzwts7G-Sppsrt22Lg,2528
|
|
41
41
|
oagi/handler/_macos.py,sha256=Gs8GrhA_WAyv9Yw0D41duliP32Xk6vouyMeWjWJJT90,5187
|
|
42
42
|
oagi/handler/_windows.py,sha256=MSgPDYEOetSjbn9eJDSrdzBVlUGgGsTlegaTDc4C4Ss,2828
|
|
43
43
|
oagi/handler/_ydotool.py,sha256=WjvE6RGRm8j3SEWpgfMw31aow3z3qkiMupuUHYt-QAM,2948
|
|
44
|
-
oagi/handler/async_pyautogui_action_handler.py,sha256=
|
|
45
|
-
oagi/handler/async_screenshot_maker.py,sha256=
|
|
46
|
-
oagi/handler/async_ydotool_action_handler.py,sha256=
|
|
44
|
+
oagi/handler/async_pyautogui_action_handler.py,sha256=vZLf6LyRUzPAr8hGmKQDDyXMmc3ZAhlCCN4dIfi3vnY,2245
|
|
45
|
+
oagi/handler/async_screenshot_maker.py,sha256=_myV4Rq6X_evCOuatalFSW5nsUDXi_0ej0GQ7V4n3JE,1856
|
|
46
|
+
oagi/handler/async_ydotool_action_handler.py,sha256=HB4QQk3OaG08g37eLb3EwsnkWKWPrpDei0ZsnBxrGZY,2159
|
|
47
47
|
oagi/handler/capslock_manager.py,sha256=40LzWt1_1wbncF5koUTdbd9V3eo5Ex_mEWwjtEmHAf4,1878
|
|
48
|
-
oagi/handler/pil_image.py,sha256=
|
|
49
|
-
oagi/handler/pyautogui_action_handler.py,sha256=
|
|
50
|
-
oagi/handler/
|
|
51
|
-
oagi/handler/
|
|
52
|
-
oagi/handler/
|
|
53
|
-
oagi/handler/
|
|
48
|
+
oagi/handler/pil_image.py,sha256=s8UGZ6ALbmOxRO2GL1EUFN7_6ZEFseSE9OHABCe7wek,5380
|
|
49
|
+
oagi/handler/pyautogui_action_handler.py,sha256=vjCtv6VaW0NNrId7PZR0-Y2VWe701t6KptQmun57aFo,11737
|
|
50
|
+
oagi/handler/screen_manager.py,sha256=FV0-6ZyTVv9yZlAg4Krga0xW9O_LDsk1iaCJjWgET-g,6565
|
|
51
|
+
oagi/handler/screenshot_maker.py,sha256=740k7NjDRKW6KwVqy_nVoczgVuw9_yTKM0gLFKB1iNs,1600
|
|
52
|
+
oagi/handler/utils.py,sha256=vfOe5H6kb50WhzxVc9Gv3i1rVXTkmv2L8fQ3mb5ECb4,1235
|
|
53
|
+
oagi/handler/wayland_support.py,sha256=qUIAQMqc3wp1VIypVmZjFDYT8t0yH0QvikTTV8pD-XA,7905
|
|
54
|
+
oagi/handler/ydotool_action_handler.py,sha256=28aUeu6F4r8oWSiZNHLdKPGQYTMJS6sOzLphQSmi7-4,9698
|
|
54
55
|
oagi/server/__init__.py,sha256=uZx8u3vJUb87kkNzwmmVrgAgbqRu0WxyMIQCLSx56kk,452
|
|
55
56
|
oagi/server/agent_wrappers.py,sha256=j8va0A7u80bzOM82nndAplK1uaO_T3kufHWScK6kfWM,3263
|
|
56
57
|
oagi/server/config.py,sha256=AJ1PLKuxrc6pRuur1hm5DwG2g2otxPwOCfKgzIACkSk,1691
|
|
@@ -75,8 +76,8 @@ oagi/types/models/step.py,sha256=RSI4H_2rrUBq_xyCoWKaq7JHdJWNobtQppaKC1l0aWU,471
|
|
|
75
76
|
oagi/utils/__init__.py,sha256=vHXyX66hEsf33OJJkmZSUjaTYU0UngfbtjcZgxfOj3A,441
|
|
76
77
|
oagi/utils/output_parser.py,sha256=U7vzmoD8pyzDg23z3vy-L9a_jKPsAlr3x8lIdPszrY8,5322
|
|
77
78
|
oagi/utils/prompt_builder.py,sha256=_Q1HY82YUrq3jSCTZ3Rszu3qmI3Wn_fmq8hf14NuwQM,2180
|
|
78
|
-
oagi_core-0.
|
|
79
|
-
oagi_core-0.
|
|
80
|
-
oagi_core-0.
|
|
81
|
-
oagi_core-0.
|
|
82
|
-
oagi_core-0.
|
|
79
|
+
oagi_core-0.14.1.dist-info/METADATA,sha256=r-RhbjurIDIEWOnL7rKM_nmGb5A7pSs4QH9h0HtsRZo,16854
|
|
80
|
+
oagi_core-0.14.1.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
81
|
+
oagi_core-0.14.1.dist-info/entry_points.txt,sha256=zzgsOSWX6aN3KUB0Z1it8DMxFFBJBqmZVqMVAJRjYuw,44
|
|
82
|
+
oagi_core-0.14.1.dist-info/licenses/LICENSE,sha256=sy5DLA2M29jFT4UfWsuBF9BAr3FnRkYtnAu6oDZiIf8,1075
|
|
83
|
+
oagi_core-0.14.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|