agentremote-cli 1.0.0__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.
- agentremote_cli-1.0.0.dist-info/METADATA +7 -0
- agentremote_cli-1.0.0.dist-info/RECORD +25 -0
- agentremote_cli-1.0.0.dist-info/WHEEL +5 -0
- agentremote_cli-1.0.0.dist-info/entry_points.txt +2 -0
- agentremote_cli-1.0.0.dist-info/top_level.txt +1 -0
- remotecli/__init__.py +1 -0
- remotecli/agent.py +673 -0
- remotecli/agent_cache.py +274 -0
- remotecli/agent_modes.py +5 -0
- remotecli/agent_open.py +323 -0
- remotecli/agent_projection.py +308 -0
- remotecli/agent_result.py +81 -0
- remotecli/agent_utils.py +113 -0
- remotecli/agent_window.py +249 -0
- remotecli/cli.py +635 -0
- remotecli/client.py +120 -0
- remotecli/config/agentremote.cli.toml +6 -0
- remotecli/cua.py +213 -0
- remotecli/guides/GUIDE.md +397 -0
- remotecli/guides/agent.md +60 -0
- remotecli/guides/cua.md +60 -0
- remotecli/guides/rpa.md +45 -0
- remotecli/guides/shell.md +49 -0
- remotecli/rpa.py +57 -0
- remotecli/shell.py +71 -0
remotecli/cli.py
ADDED
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
"""
|
|
2
|
+
remotecli — AgentRemote 命令行工具.
|
|
3
|
+
|
|
4
|
+
Agent 通过此 CLI 与 Middleware 交互:
|
|
5
|
+
remotecli cua click --pid 17376 --window-id 7014188 --target 保存
|
|
6
|
+
remotecli rpa run --flow crm-transaction-filter --param START_DATE=20260701 --param END_DATE=20260701
|
|
7
|
+
remotecli result <trace_id>
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
import json
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any, Dict
|
|
17
|
+
|
|
18
|
+
_RUNTIME_ROOT = Path(__file__).resolve().parents[1]
|
|
19
|
+
_SHARED_ROOT = _RUNTIME_ROOT / "shared"
|
|
20
|
+
if str(_SHARED_ROOT) not in sys.path:
|
|
21
|
+
sys.path.insert(0, str(_SHARED_ROOT))
|
|
22
|
+
|
|
23
|
+
from agentremote_common.config import load_cli_defaults
|
|
24
|
+
|
|
25
|
+
from .client import RemoteCliClient
|
|
26
|
+
from .cua import CuaCommands
|
|
27
|
+
from .agent import AgentCommands
|
|
28
|
+
from .rpa import RpaCommands
|
|
29
|
+
from .shell import ShellCommands
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _configure_stdio() -> None:
|
|
33
|
+
for stream_name in ("stdout", "stderr"):
|
|
34
|
+
stream = getattr(sys, stream_name, None)
|
|
35
|
+
if hasattr(stream, "reconfigure"):
|
|
36
|
+
try:
|
|
37
|
+
stream.reconfigure(encoding="utf-8", errors="replace")
|
|
38
|
+
except Exception:
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _print_json(data: Dict[str, Any]) -> None:
|
|
43
|
+
text = json.dumps(data, ensure_ascii=False, indent=2)
|
|
44
|
+
try:
|
|
45
|
+
print(text)
|
|
46
|
+
except UnicodeEncodeError:
|
|
47
|
+
print(json.dumps(data, ensure_ascii=True, indent=2))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _maybe_wait_for_result(
|
|
51
|
+
client: RemoteCliClient,
|
|
52
|
+
submitted: Dict[str, Any],
|
|
53
|
+
wait: bool,
|
|
54
|
+
timeout_sec: float,
|
|
55
|
+
interval: float,
|
|
56
|
+
) -> Dict[str, Any]:
|
|
57
|
+
if not wait:
|
|
58
|
+
return submitted
|
|
59
|
+
trace_id = submitted.get("trace_id")
|
|
60
|
+
if not trace_id:
|
|
61
|
+
return submitted
|
|
62
|
+
return client.poll_result(trace_id, timeout_sec=timeout_sec, interval=interval)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _split_targets(value: str) -> list[str]:
|
|
66
|
+
return [part.strip() for part in value.split(",") if part.strip()]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _parse_param_flags(values: list[str]) -> Dict[str, str]:
|
|
70
|
+
params: Dict[str, str] = {}
|
|
71
|
+
for value in values:
|
|
72
|
+
if "=" not in value:
|
|
73
|
+
raise ValueError(f"RPA --param must be KEY=VALUE: {value}")
|
|
74
|
+
key, param_value = value.split("=", 1)
|
|
75
|
+
key = key.strip()
|
|
76
|
+
if not key:
|
|
77
|
+
raise ValueError(f"RPA --param key cannot be empty: {value}")
|
|
78
|
+
params[key] = param_value
|
|
79
|
+
return params
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _add_agent_window_cache_args(parser: argparse.ArgumentParser) -> None:
|
|
83
|
+
parser.add_argument("--session", default="",
|
|
84
|
+
help="Task session id for reusing cached app pid/window_id")
|
|
85
|
+
parser.add_argument("--refresh-window", action="store_true",
|
|
86
|
+
help="Ignore cached pid/window_id and resolve the app window again")
|
|
87
|
+
parser.add_argument("--no-window-cache", action="store_true",
|
|
88
|
+
help="Disable session window cache for this command")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def main() -> None:
|
|
92
|
+
_configure_stdio()
|
|
93
|
+
pre_parser = argparse.ArgumentParser(add_help=False)
|
|
94
|
+
pre_parser.add_argument("--config", default="")
|
|
95
|
+
pre_args, _ = pre_parser.parse_known_args()
|
|
96
|
+
defaults = load_cli_defaults(pre_args.config)
|
|
97
|
+
|
|
98
|
+
parser = argparse.ArgumentParser(
|
|
99
|
+
description="AgentRemote CLI — capability-first remote control",
|
|
100
|
+
prog="remotecli",
|
|
101
|
+
)
|
|
102
|
+
parser.add_argument("--config", default=pre_args.config,
|
|
103
|
+
help="AgentRemote CLI TOML config path")
|
|
104
|
+
parser.add_argument("--middleware", default=defaults.middleware_url,
|
|
105
|
+
help="Middleware HTTP URL")
|
|
106
|
+
parser.add_argument("--tenant", default=defaults.tenant, help="Tenant ID")
|
|
107
|
+
parser.add_argument("--node", default=defaults.node, help="Node ID")
|
|
108
|
+
parser.add_argument("--agent", default=defaults.agent, help="Agent ID")
|
|
109
|
+
parser.add_argument("--handle", default=defaults.handle, help="Auth broker handle (wch_v1_...)")
|
|
110
|
+
parser.add_argument("--wait", dest="wait_result", action="store_true",
|
|
111
|
+
help="Wait for submitted task result before printing")
|
|
112
|
+
parser.add_argument("--wait-timeout", type=float, default=30.0,
|
|
113
|
+
help="Seconds to wait for task result when --wait is set")
|
|
114
|
+
parser.add_argument("--poll-interval", type=float, default=0.1,
|
|
115
|
+
help="Seconds between result polls when --wait is set")
|
|
116
|
+
|
|
117
|
+
sub = parser.add_subparsers(dest="command", help="Subcommands")
|
|
118
|
+
|
|
119
|
+
# ── cua ──────────────────────────────
|
|
120
|
+
cua_parser = sub.add_parser("cua", help="CUA desktop control")
|
|
121
|
+
cua_sub = cua_parser.add_subparsers(dest="cua_action", help="CUA actions")
|
|
122
|
+
|
|
123
|
+
# cua click
|
|
124
|
+
p = cua_sub.add_parser("click", help="Click an element")
|
|
125
|
+
p.add_argument("--pid", type=int, required=True)
|
|
126
|
+
p.add_argument("--window-id", type=int, required=True)
|
|
127
|
+
p.add_argument("--target", default="")
|
|
128
|
+
p.add_argument("--x", type=int, default=0)
|
|
129
|
+
p.add_argument("--y", type=int, default=0)
|
|
130
|
+
p.add_argument("--element-index", type=int, default=0)
|
|
131
|
+
p.add_argument("--locate-mode", choices=["smart", "uia", "som"], default="",
|
|
132
|
+
help="Target resolution mode: smart=UIA then SOM/OCR, uia=UIA only, som=SOM/OCR only")
|
|
133
|
+
|
|
134
|
+
# cua double-click
|
|
135
|
+
p = cua_sub.add_parser("double-click", help="Double-click an element")
|
|
136
|
+
p.add_argument("--pid", type=int, required=True)
|
|
137
|
+
p.add_argument("--window-id", type=int, required=True)
|
|
138
|
+
p.add_argument("--target", default="")
|
|
139
|
+
p.add_argument("--locate-mode", choices=["smart", "uia", "som"], default="",
|
|
140
|
+
help="Target resolution mode")
|
|
141
|
+
|
|
142
|
+
# cua right-click
|
|
143
|
+
p = cua_sub.add_parser("right-click", help="Right-click an element")
|
|
144
|
+
p.add_argument("--pid", type=int, required=True)
|
|
145
|
+
p.add_argument("--window-id", type=int, required=True)
|
|
146
|
+
p.add_argument("--target", default="")
|
|
147
|
+
p.add_argument("--locate-mode", choices=["smart", "uia", "som"], default="",
|
|
148
|
+
help="Target resolution mode")
|
|
149
|
+
|
|
150
|
+
# cua type-text
|
|
151
|
+
p = cua_sub.add_parser("type-text", help="Type text into an element")
|
|
152
|
+
p.add_argument("--pid", type=int, required=True)
|
|
153
|
+
p.add_argument("--window-id", type=int, default=0)
|
|
154
|
+
p.add_argument("--target", required=True)
|
|
155
|
+
p.add_argument("--text", required=True)
|
|
156
|
+
p.add_argument("--locate-mode", choices=["smart", "uia", "som"], default="",
|
|
157
|
+
help="Target resolution mode")
|
|
158
|
+
|
|
159
|
+
# cua hotkey
|
|
160
|
+
p = cua_sub.add_parser("hotkey", help="Send hotkey combination")
|
|
161
|
+
p.add_argument("--pid", type=int, required=True)
|
|
162
|
+
p.add_argument("--window-id", type=int, default=0)
|
|
163
|
+
p.add_argument("--keys", required=True, help="e.g. ctrl,s")
|
|
164
|
+
|
|
165
|
+
# cua press-key
|
|
166
|
+
p = cua_sub.add_parser("press-key", help="Press a single key")
|
|
167
|
+
p.add_argument("--pid", type=int, required=True)
|
|
168
|
+
p.add_argument("--window-id", type=int, default=0)
|
|
169
|
+
p.add_argument("--key", required=True, help="e.g. enter, tab")
|
|
170
|
+
|
|
171
|
+
# cua launch-app
|
|
172
|
+
p = cua_sub.add_parser("launch-app", help="Launch an application")
|
|
173
|
+
p.add_argument("--name", default="")
|
|
174
|
+
p.add_argument("--path", dest="launch_path", default="")
|
|
175
|
+
p.add_argument("--args", dest="app_args", default="")
|
|
176
|
+
p.add_argument("--wait", dest="app_wait", action="store_true",
|
|
177
|
+
help="Wait for launched app window before returning")
|
|
178
|
+
|
|
179
|
+
# cua list-apps
|
|
180
|
+
p = cua_sub.add_parser("list-apps", help="List running applications")
|
|
181
|
+
p.add_argument("--query", default="")
|
|
182
|
+
p.add_argument("--max-results", type=int, default=0)
|
|
183
|
+
|
|
184
|
+
# cua list-windows
|
|
185
|
+
p = cua_sub.add_parser("list-windows", help="List windows")
|
|
186
|
+
p.add_argument("--pid", type=int, default=0)
|
|
187
|
+
|
|
188
|
+
# cua bring-to-front
|
|
189
|
+
p = cua_sub.add_parser("bring-to-front", help="Bring window to front")
|
|
190
|
+
p.add_argument("--pid", type=int, required=True)
|
|
191
|
+
p.add_argument("--window-id", type=int, required=True)
|
|
192
|
+
|
|
193
|
+
# cua get-window-state
|
|
194
|
+
p = cua_sub.add_parser("get-window-state", help="Get window state")
|
|
195
|
+
p.add_argument("--pid", type=int, required=True)
|
|
196
|
+
p.add_argument("--window-id", type=int, required=True)
|
|
197
|
+
p.add_argument("--mode", choices=["uia", "vision", "som-full"], default="vision",
|
|
198
|
+
help="uia=UIA tree, vision=screenshot only, som-full=full YOLO/OCR annotated snapshot")
|
|
199
|
+
|
|
200
|
+
# cua probe-window
|
|
201
|
+
p = cua_sub.add_parser("probe-window", help="Probe target labels in one window")
|
|
202
|
+
p.add_argument("--pid", type=int, required=True)
|
|
203
|
+
p.add_argument("--window-id", type=int, required=True)
|
|
204
|
+
p.add_argument("--targets", required=True,
|
|
205
|
+
help="Comma-separated target labels to probe")
|
|
206
|
+
p.add_argument("--mode", choices=["smart", "uia", "som"], default="smart",
|
|
207
|
+
help="smart=UIA then SOM/OCR, uia=UIA only, som=SOM/OCR only")
|
|
208
|
+
|
|
209
|
+
# cua get-cursor-position
|
|
210
|
+
cua_sub.add_parser("get-cursor-position", help="Get current cursor position")
|
|
211
|
+
|
|
212
|
+
# cua move-mouse
|
|
213
|
+
p = cua_sub.add_parser("move-mouse", help="Move mouse to absolute screen coordinates")
|
|
214
|
+
p.add_argument("--x", type=int, required=True)
|
|
215
|
+
p.add_argument("--y", type=int, required=True)
|
|
216
|
+
|
|
217
|
+
# cua move-mouse-relative
|
|
218
|
+
p = cua_sub.add_parser("move-mouse-relative", help="Move mouse relative to current position")
|
|
219
|
+
p.add_argument("--dx", type=int, required=True)
|
|
220
|
+
p.add_argument("--dy", type=int, required=True)
|
|
221
|
+
|
|
222
|
+
# cua scroll
|
|
223
|
+
p = cua_sub.add_parser("scroll", help="Scroll in a window")
|
|
224
|
+
p.add_argument("--pid", type=int, required=True)
|
|
225
|
+
p.add_argument("--window-id", type=int, required=True)
|
|
226
|
+
p.add_argument("--dx", type=int, default=0)
|
|
227
|
+
p.add_argument("--dy", type=int, default=0)
|
|
228
|
+
p.add_argument("--direction", choices=["up", "down", "left", "right"], default="",
|
|
229
|
+
help="Scroll direction; preferred over dx/dy")
|
|
230
|
+
p.add_argument("--amount", type=int, default=0,
|
|
231
|
+
help="Number of scroll ticks when --direction is used")
|
|
232
|
+
p.add_argument("--by", choices=["line", "page"], default="",
|
|
233
|
+
help="Scroll granularity when --direction is used")
|
|
234
|
+
p.add_argument("--dispatch", choices=["background", "foreground", "auto"], default="",
|
|
235
|
+
help="Driver dispatch mode")
|
|
236
|
+
|
|
237
|
+
# cua drag
|
|
238
|
+
p = cua_sub.add_parser("drag", help="Drag from one point to another")
|
|
239
|
+
p.add_argument("--pid", type=int, required=True)
|
|
240
|
+
p.add_argument("--window-id", type=int, required=True)
|
|
241
|
+
p.add_argument("--from-x", type=int, default=0)
|
|
242
|
+
p.add_argument("--from-y", type=int, default=0)
|
|
243
|
+
p.add_argument("--to-x", type=int, required=True)
|
|
244
|
+
p.add_argument("--to-y", type=int, required=True)
|
|
245
|
+
|
|
246
|
+
# cua set-value
|
|
247
|
+
p = cua_sub.add_parser("set-value", help="Set value of a UI element")
|
|
248
|
+
p.add_argument("--pid", type=int, required=True)
|
|
249
|
+
p.add_argument("--window-id", type=int, default=0)
|
|
250
|
+
p.add_argument("--target", default="")
|
|
251
|
+
p.add_argument("--element-index", type=int, default=0)
|
|
252
|
+
p.add_argument("--value", required=True)
|
|
253
|
+
p.add_argument("--locate-mode", choices=["smart", "uia", "som"], default="",
|
|
254
|
+
help="Target resolution mode")
|
|
255
|
+
|
|
256
|
+
# ── rpa ──────────────────────────────
|
|
257
|
+
rpa_parser = sub.add_parser("rpa", help="RPA flow execution")
|
|
258
|
+
rpa_sub = rpa_parser.add_subparsers(dest="rpa_action", help="RPA actions")
|
|
259
|
+
|
|
260
|
+
p = rpa_sub.add_parser("run", help="Run an RPA flow")
|
|
261
|
+
p.add_argument("--flow", required=True, help="Flow ID")
|
|
262
|
+
p.add_argument("--task", default="", help="Task name")
|
|
263
|
+
p.add_argument("--param", action="append", default=[],
|
|
264
|
+
help="Robot variable in KEY=VALUE form; can be repeated")
|
|
265
|
+
|
|
266
|
+
p = rpa_sub.add_parser("list-flows", help="List available RPA flows")
|
|
267
|
+
|
|
268
|
+
# ── shell ────────────────────────────
|
|
269
|
+
shell_parser = sub.add_parser("shell", help="Remote shell execution")
|
|
270
|
+
shell_sub = shell_parser.add_subparsers(dest="shell_action", help="Shell actions")
|
|
271
|
+
|
|
272
|
+
# shell exec
|
|
273
|
+
p = shell_sub.add_parser("exec", help="Execute a shell command")
|
|
274
|
+
p.add_argument("--cmd", required=True, help="Command to execute")
|
|
275
|
+
p.add_argument("--cwd", default="", help="Working directory")
|
|
276
|
+
p.add_argument("--timeout", type=int, default=60, help="Timeout in seconds")
|
|
277
|
+
|
|
278
|
+
# shell exec-script
|
|
279
|
+
p = shell_sub.add_parser("exec-script", help="Execute a multi-line script")
|
|
280
|
+
p.add_argument("--script", required=True, help="Script content")
|
|
281
|
+
p.add_argument("--shell", default="", help="Shell type: cmd / powershell / bash / sh")
|
|
282
|
+
p.add_argument("--cwd", default="", help="Working directory")
|
|
283
|
+
p.add_argument("--timeout", type=int, default=60, help="Timeout in seconds")
|
|
284
|
+
|
|
285
|
+
# shell info
|
|
286
|
+
shell_sub.add_parser("info", help="Get shell/OS environment info")
|
|
287
|
+
|
|
288
|
+
# ── agent ────────────────────────────
|
|
289
|
+
agent_parser = sub.add_parser("agent", help="Agent-friendly high-level wrappers")
|
|
290
|
+
agent_sub = agent_parser.add_subparsers(dest="agent_action", help="Agent actions")
|
|
291
|
+
|
|
292
|
+
p = agent_sub.add_parser("ps", help="Run PowerShell and print unpacked result")
|
|
293
|
+
p.add_argument("script", help="PowerShell script")
|
|
294
|
+
p.add_argument("--cwd", default="", help="Working directory")
|
|
295
|
+
p.add_argument("--timeout", type=int, default=60, help="Remote command timeout in seconds")
|
|
296
|
+
|
|
297
|
+
p = agent_sub.add_parser("cmd", help="Run cmd.exe command and print unpacked result")
|
|
298
|
+
p.add_argument("cmd", help="Command to execute")
|
|
299
|
+
p.add_argument("--cwd", default="", help="Working directory")
|
|
300
|
+
p.add_argument("--timeout", type=int, default=60, help="Remote command timeout in seconds")
|
|
301
|
+
|
|
302
|
+
p = agent_sub.add_parser("open", help="Ensure an app main window exists and cache pid/window_id")
|
|
303
|
+
p.add_argument("app", help="Application query, e.g. firefox or 微信")
|
|
304
|
+
p.add_argument("--path", dest="launch_path", default="",
|
|
305
|
+
help="Optional explicit executable path")
|
|
306
|
+
p.add_argument("--args", dest="app_args", default="",
|
|
307
|
+
help="Optional launch arguments")
|
|
308
|
+
p.add_argument("--timeout", dest="launch_timeout", type=int, default=15,
|
|
309
|
+
help="Seconds to wait for a main window after launch")
|
|
310
|
+
p.add_argument("--no-start", action="store_true",
|
|
311
|
+
help="Only find an existing main window; do not launch")
|
|
312
|
+
p.add_argument("--no-foreground", action="store_true",
|
|
313
|
+
help="Do not try to bring the main window to front")
|
|
314
|
+
_add_agent_window_cache_args(p)
|
|
315
|
+
|
|
316
|
+
p = agent_sub.add_parser("observe", help="Find an app window and return its state")
|
|
317
|
+
p.add_argument("app", help="Application query, e.g. firefox")
|
|
318
|
+
p.add_argument("--mode", choices=["uia", "vision", "som-full"], default="uia",
|
|
319
|
+
help="uia=UIA tree, vision=screenshot only, som-full=full YOLO/OCR annotated snapshot")
|
|
320
|
+
p.add_argument("--format", dest="observe_format", choices=["compact", "full"], default="compact",
|
|
321
|
+
help="Return compact AI-facing state or full raw state")
|
|
322
|
+
p.add_argument("--element-limit", type=int, default=20,
|
|
323
|
+
help="Maximum compact elements to return")
|
|
324
|
+
p.add_argument("--targets", default="",
|
|
325
|
+
help="Comma-separated target labels to prioritize in compact output")
|
|
326
|
+
_add_agent_window_cache_args(p)
|
|
327
|
+
|
|
328
|
+
p = agent_sub.add_parser("probe", help="Find an app window and probe target labels")
|
|
329
|
+
p.add_argument("app", help="Application query, e.g. firefox")
|
|
330
|
+
p.add_argument("--targets", required=True,
|
|
331
|
+
help="Comma-separated target labels to probe")
|
|
332
|
+
p.add_argument("--mode", choices=["smart", "uia", "som"], default="smart",
|
|
333
|
+
help="smart=UIA then SOM/OCR, uia=UIA only, som=SOM/OCR only")
|
|
334
|
+
_add_agent_window_cache_args(p)
|
|
335
|
+
|
|
336
|
+
p = agent_sub.add_parser("click", help="Find an app window and click target text")
|
|
337
|
+
p.add_argument("app", help="Application query, e.g. firefox")
|
|
338
|
+
p.add_argument("target", help="Target text")
|
|
339
|
+
p.add_argument("--locate-mode", choices=["smart", "uia", "som"], default="smart",
|
|
340
|
+
help="Target resolution mode: smart=UIA then SOM/OCR, uia=UIA only, som=SOM/OCR only")
|
|
341
|
+
_add_agent_window_cache_args(p)
|
|
342
|
+
|
|
343
|
+
p = agent_sub.add_parser("type", help="Find an app window and type text into target")
|
|
344
|
+
p.add_argument("app", help="Application query, e.g. firefox")
|
|
345
|
+
p.add_argument("target", help="Target text")
|
|
346
|
+
p.add_argument("text", help="Text to type")
|
|
347
|
+
p.add_argument("--locate-mode", choices=["smart", "uia", "som"], default="smart",
|
|
348
|
+
help="Target resolution mode: smart=UIA then SOM/OCR, uia=UIA only, som=SOM/OCR only")
|
|
349
|
+
_add_agent_window_cache_args(p)
|
|
350
|
+
|
|
351
|
+
p = agent_sub.add_parser("key", help="Find an app window and press one key")
|
|
352
|
+
p.add_argument("app", help="Application query, e.g. firefox")
|
|
353
|
+
p.add_argument("key", help="Key name, e.g. escape")
|
|
354
|
+
_add_agent_window_cache_args(p)
|
|
355
|
+
|
|
356
|
+
p = agent_sub.add_parser("hotkey", help="Find an app window and send a hotkey")
|
|
357
|
+
p.add_argument("app", help="Application query, e.g. firefox")
|
|
358
|
+
p.add_argument("keys", help="Keys, e.g. ctrl,l or ctrl+shift+s")
|
|
359
|
+
_add_agent_window_cache_args(p)
|
|
360
|
+
|
|
361
|
+
p = agent_sub.add_parser("scroll", help="Find an app window and scroll it")
|
|
362
|
+
p.add_argument("app", help="Application query, e.g. firefox")
|
|
363
|
+
p.add_argument("--direction", choices=["up", "down", "left", "right"], default="down")
|
|
364
|
+
p.add_argument("--amount", type=int, default=3)
|
|
365
|
+
p.add_argument("--by", choices=["line", "page"], default="line")
|
|
366
|
+
p.add_argument("--dispatch", choices=["background", "foreground", "auto"], default="")
|
|
367
|
+
_add_agent_window_cache_args(p)
|
|
368
|
+
|
|
369
|
+
p = agent_sub.add_parser("session-clear", help="Clear cached agent window refs")
|
|
370
|
+
p.add_argument("session", nargs="?", default="",
|
|
371
|
+
help="Task session id; falls back to REMOTECLI_SESSION")
|
|
372
|
+
|
|
373
|
+
# ── result ───────────────────────────
|
|
374
|
+
p = sub.add_parser("result", help="Query task result")
|
|
375
|
+
p.add_argument("trace_id", help="Trace ID")
|
|
376
|
+
|
|
377
|
+
args = parser.parse_args()
|
|
378
|
+
|
|
379
|
+
client = RemoteCliClient(
|
|
380
|
+
middleware_url=args.middleware,
|
|
381
|
+
tenant_id=args.tenant,
|
|
382
|
+
node_id=args.node,
|
|
383
|
+
agent_id=args.agent,
|
|
384
|
+
handle=args.handle,
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
if args.command == "cua":
|
|
388
|
+
cua = CuaCommands(client)
|
|
389
|
+
action = args.cua_action
|
|
390
|
+
if action == "click":
|
|
391
|
+
result = cua.click(
|
|
392
|
+
args.pid,
|
|
393
|
+
args.window_id,
|
|
394
|
+
args.target,
|
|
395
|
+
args.x,
|
|
396
|
+
args.y,
|
|
397
|
+
args.element_index,
|
|
398
|
+
locate_mode=args.locate_mode,
|
|
399
|
+
)
|
|
400
|
+
elif action == "double-click":
|
|
401
|
+
result = cua.double_click(args.pid, args.window_id, args.target, locate_mode=args.locate_mode)
|
|
402
|
+
elif action == "right-click":
|
|
403
|
+
result = cua.right_click(args.pid, args.window_id, args.target, locate_mode=args.locate_mode)
|
|
404
|
+
elif action == "type-text":
|
|
405
|
+
result = cua.type_text(
|
|
406
|
+
args.pid,
|
|
407
|
+
args.target,
|
|
408
|
+
args.text,
|
|
409
|
+
window_id=args.window_id,
|
|
410
|
+
locate_mode=args.locate_mode,
|
|
411
|
+
)
|
|
412
|
+
elif action == "hotkey":
|
|
413
|
+
result = cua.hotkey(args.pid, args.keys, window_id=args.window_id)
|
|
414
|
+
elif action == "press-key":
|
|
415
|
+
result = cua.press_key(args.pid, args.key, window_id=args.window_id)
|
|
416
|
+
elif action == "launch-app":
|
|
417
|
+
result = cua.launch_app(args.name, args.launch_path, args.app_args, args.app_wait)
|
|
418
|
+
elif action == "list-apps":
|
|
419
|
+
result = cua.list_apps(args.query, args.max_results)
|
|
420
|
+
elif action == "list-windows":
|
|
421
|
+
result = cua.list_windows(args.pid)
|
|
422
|
+
elif action == "bring-to-front":
|
|
423
|
+
result = cua.bring_to_front(args.pid, args.window_id)
|
|
424
|
+
elif action == "get-window-state":
|
|
425
|
+
result = cua.get_window_state(args.pid, args.window_id, args.mode)
|
|
426
|
+
elif action == "probe-window":
|
|
427
|
+
result = cua.probe_window(args.pid, args.window_id, _split_targets(args.targets), mode=args.mode)
|
|
428
|
+
elif action == "get-cursor-position":
|
|
429
|
+
result = cua.get_cursor_position()
|
|
430
|
+
elif action == "move-mouse":
|
|
431
|
+
result = cua.move_mouse(args.x, args.y)
|
|
432
|
+
elif action == "move-mouse-relative":
|
|
433
|
+
result = cua.move_mouse_relative(args.dx, args.dy)
|
|
434
|
+
elif action == "scroll":
|
|
435
|
+
result = cua.scroll(
|
|
436
|
+
args.pid,
|
|
437
|
+
args.window_id,
|
|
438
|
+
args.dx,
|
|
439
|
+
args.dy,
|
|
440
|
+
direction=args.direction,
|
|
441
|
+
amount=args.amount,
|
|
442
|
+
by=args.by,
|
|
443
|
+
dispatch=args.dispatch,
|
|
444
|
+
)
|
|
445
|
+
elif action == "drag":
|
|
446
|
+
result = cua.drag(args.pid, args.window_id, args.from_x, args.from_y, args.to_x, args.to_y)
|
|
447
|
+
elif action == "set-value":
|
|
448
|
+
result = cua.set_value(
|
|
449
|
+
args.pid,
|
|
450
|
+
args.target,
|
|
451
|
+
args.value,
|
|
452
|
+
args.element_index,
|
|
453
|
+
args.window_id,
|
|
454
|
+
locate_mode=args.locate_mode,
|
|
455
|
+
)
|
|
456
|
+
else:
|
|
457
|
+
print(f"Unknown cua action: {action}", file=sys.stderr)
|
|
458
|
+
sys.exit(1)
|
|
459
|
+
_print_json(_maybe_wait_for_result(
|
|
460
|
+
client, result, args.wait_result, args.wait_timeout, args.poll_interval
|
|
461
|
+
))
|
|
462
|
+
|
|
463
|
+
elif args.command == "rpa":
|
|
464
|
+
rpa = RpaCommands(client)
|
|
465
|
+
action = args.rpa_action
|
|
466
|
+
if action == "run":
|
|
467
|
+
result = rpa.run(args.flow, args.task, params=_parse_param_flags(args.param))
|
|
468
|
+
elif action == "list-flows":
|
|
469
|
+
result = rpa.list_flows()
|
|
470
|
+
else:
|
|
471
|
+
print(f"Unknown rpa action: {action}", file=sys.stderr)
|
|
472
|
+
sys.exit(1)
|
|
473
|
+
_print_json(_maybe_wait_for_result(
|
|
474
|
+
client, result, args.wait_result, args.wait_timeout, args.poll_interval
|
|
475
|
+
))
|
|
476
|
+
|
|
477
|
+
elif args.command == "shell":
|
|
478
|
+
shell = ShellCommands(client)
|
|
479
|
+
action = args.shell_action
|
|
480
|
+
if action == "exec":
|
|
481
|
+
result = shell.exec(args.cmd, args.cwd, args.timeout)
|
|
482
|
+
elif action == "exec-script":
|
|
483
|
+
result = shell.exec_script(args.script, args.shell, args.cwd, args.timeout)
|
|
484
|
+
elif action == "info":
|
|
485
|
+
result = shell.info()
|
|
486
|
+
else:
|
|
487
|
+
print(f"Unknown shell action: {action}", file=sys.stderr)
|
|
488
|
+
sys.exit(1)
|
|
489
|
+
_print_json(_maybe_wait_for_result(
|
|
490
|
+
client, result, args.wait_result, args.wait_timeout, args.poll_interval
|
|
491
|
+
))
|
|
492
|
+
|
|
493
|
+
elif args.command == "agent":
|
|
494
|
+
agent = AgentCommands(client)
|
|
495
|
+
action = args.agent_action
|
|
496
|
+
if action == "ps":
|
|
497
|
+
result = agent.ps(
|
|
498
|
+
args.script,
|
|
499
|
+
cwd=args.cwd,
|
|
500
|
+
timeout_sec=args.timeout,
|
|
501
|
+
wait_timeout=args.wait_timeout,
|
|
502
|
+
poll_interval=args.poll_interval,
|
|
503
|
+
)
|
|
504
|
+
elif action == "cmd":
|
|
505
|
+
result = agent.cmd(
|
|
506
|
+
args.cmd,
|
|
507
|
+
cwd=args.cwd,
|
|
508
|
+
timeout_sec=args.timeout,
|
|
509
|
+
wait_timeout=args.wait_timeout,
|
|
510
|
+
poll_interval=args.poll_interval,
|
|
511
|
+
)
|
|
512
|
+
elif action == "open":
|
|
513
|
+
result = agent.open(
|
|
514
|
+
args.app,
|
|
515
|
+
launch_path=args.launch_path,
|
|
516
|
+
app_args=args.app_args,
|
|
517
|
+
start_if_missing=not args.no_start,
|
|
518
|
+
foreground=not args.no_foreground,
|
|
519
|
+
launch_timeout=args.launch_timeout,
|
|
520
|
+
wait_timeout=args.wait_timeout,
|
|
521
|
+
poll_interval=args.poll_interval,
|
|
522
|
+
session_id=args.session,
|
|
523
|
+
use_window_cache=not args.no_window_cache,
|
|
524
|
+
)
|
|
525
|
+
elif action == "observe":
|
|
526
|
+
result = agent.observe(
|
|
527
|
+
args.app,
|
|
528
|
+
mode=args.mode,
|
|
529
|
+
output_format=args.observe_format,
|
|
530
|
+
element_limit=args.element_limit,
|
|
531
|
+
targets=_split_targets(args.targets),
|
|
532
|
+
wait_timeout=args.wait_timeout,
|
|
533
|
+
poll_interval=args.poll_interval,
|
|
534
|
+
session_id=args.session,
|
|
535
|
+
use_window_cache=not args.no_window_cache,
|
|
536
|
+
refresh_window=args.refresh_window,
|
|
537
|
+
)
|
|
538
|
+
elif action == "probe":
|
|
539
|
+
result = agent.probe(
|
|
540
|
+
args.app,
|
|
541
|
+
_split_targets(args.targets),
|
|
542
|
+
mode=args.mode,
|
|
543
|
+
wait_timeout=args.wait_timeout,
|
|
544
|
+
poll_interval=args.poll_interval,
|
|
545
|
+
session_id=args.session,
|
|
546
|
+
use_window_cache=not args.no_window_cache,
|
|
547
|
+
refresh_window=args.refresh_window,
|
|
548
|
+
)
|
|
549
|
+
elif action == "click":
|
|
550
|
+
result = agent.click(
|
|
551
|
+
args.app,
|
|
552
|
+
args.target,
|
|
553
|
+
locate_mode=args.locate_mode,
|
|
554
|
+
wait_timeout=args.wait_timeout,
|
|
555
|
+
poll_interval=args.poll_interval,
|
|
556
|
+
session_id=args.session,
|
|
557
|
+
use_window_cache=not args.no_window_cache,
|
|
558
|
+
refresh_window=args.refresh_window,
|
|
559
|
+
)
|
|
560
|
+
elif action == "type":
|
|
561
|
+
result = agent.type_text(
|
|
562
|
+
args.app,
|
|
563
|
+
args.target,
|
|
564
|
+
args.text,
|
|
565
|
+
locate_mode=args.locate_mode,
|
|
566
|
+
wait_timeout=args.wait_timeout,
|
|
567
|
+
poll_interval=args.poll_interval,
|
|
568
|
+
session_id=args.session,
|
|
569
|
+
use_window_cache=not args.no_window_cache,
|
|
570
|
+
refresh_window=args.refresh_window,
|
|
571
|
+
)
|
|
572
|
+
elif action == "key":
|
|
573
|
+
result = agent.key(
|
|
574
|
+
args.app,
|
|
575
|
+
args.key,
|
|
576
|
+
wait_timeout=args.wait_timeout,
|
|
577
|
+
poll_interval=args.poll_interval,
|
|
578
|
+
session_id=args.session,
|
|
579
|
+
use_window_cache=not args.no_window_cache,
|
|
580
|
+
refresh_window=args.refresh_window,
|
|
581
|
+
)
|
|
582
|
+
elif action == "hotkey":
|
|
583
|
+
result = agent.hotkey(
|
|
584
|
+
args.app,
|
|
585
|
+
args.keys,
|
|
586
|
+
wait_timeout=args.wait_timeout,
|
|
587
|
+
poll_interval=args.poll_interval,
|
|
588
|
+
session_id=args.session,
|
|
589
|
+
use_window_cache=not args.no_window_cache,
|
|
590
|
+
refresh_window=args.refresh_window,
|
|
591
|
+
)
|
|
592
|
+
elif action == "scroll":
|
|
593
|
+
result = agent.scroll(
|
|
594
|
+
args.app,
|
|
595
|
+
direction=args.direction,
|
|
596
|
+
amount=args.amount,
|
|
597
|
+
by=args.by,
|
|
598
|
+
dispatch=args.dispatch,
|
|
599
|
+
wait_timeout=args.wait_timeout,
|
|
600
|
+
poll_interval=args.poll_interval,
|
|
601
|
+
session_id=args.session,
|
|
602
|
+
use_window_cache=not args.no_window_cache,
|
|
603
|
+
refresh_window=args.refresh_window,
|
|
604
|
+
)
|
|
605
|
+
elif action == "session-clear":
|
|
606
|
+
result = agent.clear_session(args.session)
|
|
607
|
+
else:
|
|
608
|
+
print(f"Unknown agent action: {action}", file=sys.stderr)
|
|
609
|
+
sys.exit(1)
|
|
610
|
+
_print_json(result)
|
|
611
|
+
|
|
612
|
+
elif args.command == "result":
|
|
613
|
+
data = client.poll_result(
|
|
614
|
+
args.trace_id,
|
|
615
|
+
timeout_sec=args.wait_timeout,
|
|
616
|
+
interval=args.poll_interval,
|
|
617
|
+
)
|
|
618
|
+
_print_json(data)
|
|
619
|
+
|
|
620
|
+
else:
|
|
621
|
+
parser.print_help()
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
if __name__ == "__main__":
|
|
625
|
+
try:
|
|
626
|
+
main()
|
|
627
|
+
except Exception as e:
|
|
628
|
+
import traceback
|
|
629
|
+
_print_json({
|
|
630
|
+
"status": "rejected",
|
|
631
|
+
"reason": "cli_error",
|
|
632
|
+
"error": str(e),
|
|
633
|
+
"traceback": traceback.format_exc(),
|
|
634
|
+
})
|
|
635
|
+
sys.exit(1)
|