rlmness-cli 0.2.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.
- rlmagent/__init__.py +1 -0
- rlmagent/__main__.py +8 -0
- rlmagent/acp/__init__.py +1 -0
- rlmagent/acp/runtime.py +71 -0
- rlmagent/acp/server.py +598 -0
- rlmagent/acp/updates.py +128 -0
- rlmagent/agentnet.py +237 -0
- rlmagent/agents/__init__.py +1 -0
- rlmagent/agents/approval.py +90 -0
- rlmagent/agents/bridge.py +109 -0
- rlmagent/agents/budget.py +212 -0
- rlmagent/agents/child_handles.py +46 -0
- rlmagent/agents/child_mailbox.py +30 -0
- rlmagent/agents/kernel_api.py +156 -0
- rlmagent/agents/output.py +34 -0
- rlmagent/agents/patch.py +107 -0
- rlmagent/agents/prompts.py +156 -0
- rlmagent/agents/trace.py +290 -0
- rlmagent/agents/tree.py +682 -0
- rlmagent/agents/wasm_api.py +105 -0
- rlmagent/agents/workspace.py +357 -0
- rlmagent/autopilot.py +447 -0
- rlmagent/backend.py +946 -0
- rlmagent/backend_context.py +74 -0
- rlmagent/cli/__init__.py +5 -0
- rlmagent/cli/config_cmd.py +177 -0
- rlmagent/cli/health.py +156 -0
- rlmagent/cli/main.py +1194 -0
- rlmagent/cli/update.py +188 -0
- rlmagent/clipboard.py +63 -0
- rlmagent/config/__init__.py +37 -0
- rlmagent/config/config.py +297 -0
- rlmagent/config/loader.py +119 -0
- rlmagent/config/prompts.py +124 -0
- rlmagent/config/secure_input.py +213 -0
- rlmagent/config/store.py +275 -0
- rlmagent/context/__init__.py +47 -0
- rlmagent/context/budget.py +557 -0
- rlmagent/contract.py +311 -0
- rlmagent/contract_run.py +232 -0
- rlmagent/conversation.py +2547 -0
- rlmagent/cron.py +77 -0
- rlmagent/directives.py +893 -0
- rlmagent/discovery.py +356 -0
- rlmagent/execution_log.py +66 -0
- rlmagent/factory.py +189 -0
- rlmagent/factory_graph.py +413 -0
- rlmagent/harness/__init__.py +63 -0
- rlmagent/harness/contracts/__init__.py +1 -0
- rlmagent/harness/contracts/stream.py +129 -0
- rlmagent/harness/contracts/tooling.py +127 -0
- rlmagent/harness/contracts/transcript/__init__.py +68 -0
- rlmagent/harness/contracts/transcript/_base.py +29 -0
- rlmagent/harness/contracts/transcript/diagnostics.py +52 -0
- rlmagent/harness/contracts/transcript/entries.py +180 -0
- rlmagent/harness/contracts/transcript/segments.py +64 -0
- rlmagent/harness/contracts/values.py +6 -0
- rlmagent/harness/driver.py +397 -0
- rlmagent/harness/engine.py +489 -0
- rlmagent/harness/mending.py +153 -0
- rlmagent/harness/provider/__init__.py +1 -0
- rlmagent/harness/provider/base.py +27 -0
- rlmagent/harness/provider/wire.py +189 -0
- rlmagent/harness/py.typed +0 -0
- rlmagent/harness/session/__init__.py +5 -0
- rlmagent/harness/session/index.py +218 -0
- rlmagent/harness/session/lease.py +66 -0
- rlmagent/harness/session/records.py +118 -0
- rlmagent/harness/session/replay.py +165 -0
- rlmagent/harness/session/store.py +155 -0
- rlmagent/harness/session/summary.py +46 -0
- rlmagent/hints.py +47 -0
- rlmagent/history.py +92 -0
- rlmagent/hooks/__init__.py +1 -0
- rlmagent/hooks/hooks.py +1 -0
- rlmagent/instructions.py +157 -0
- rlmagent/job_runner.py +117 -0
- rlmagent/jobs.py +365 -0
- rlmagent/kept_agents.py +397 -0
- rlmagent/kernel/__init__.py +5 -0
- rlmagent/kernel/session.py +205 -0
- rlmagent/kernel/wasm.py +169 -0
- rlmagent/logs.py +208 -0
- rlmagent/managed_release.py +368 -0
- rlmagent/mcp.py +411 -0
- rlmagent/mcp_oauth.py +213 -0
- rlmagent/model/__init__.py +29 -0
- rlmagent/model/_claude/__init__.py +1 -0
- rlmagent/model/_claude/courier.py +117 -0
- rlmagent/model/_claude/emitter.py +378 -0
- rlmagent/model/_oai/__init__.py +1 -0
- rlmagent/model/_oai/helpers.py +256 -0
- rlmagent/model/_oai/normalize.py +263 -0
- rlmagent/model/_oai/parsers.py +317 -0
- rlmagent/model/_oai/payloads.py +226 -0
- rlmagent/model/_oai/transport.py +127 -0
- rlmagent/model/claude.py +552 -0
- rlmagent/model/copilot.py +142 -0
- rlmagent/model/correlation.py +21 -0
- rlmagent/model/deadline.py +19 -0
- rlmagent/model/discovery.py +181 -0
- rlmagent/model/external/__init__.py +11 -0
- rlmagent/model/external/acp.py +202 -0
- rlmagent/model/external/acp_install.py +109 -0
- rlmagent/model/external/cli.py +248 -0
- rlmagent/model/external/textcalls.py +57 -0
- rlmagent/model/failover.py +129 -0
- rlmagent/model/google.py +199 -0
- rlmagent/model/limits.py +46 -0
- rlmagent/model/oai_compatible.py +414 -0
- rlmagent/model/preferences.py +56 -0
- rlmagent/model/py.typed +0 -0
- rlmagent/model/registered_oauth.py +195 -0
- rlmagent/model/scripted.py +55 -0
- rlmagent/model/settings.py +271 -0
- rlmagent/model/subscription.py +398 -0
- rlmagent/model/token_bounds.py +15 -0
- rlmagent/model/transport/__init__.py +1 -0
- rlmagent/model/transport/backoff.py +87 -0
- rlmagent/model/transport/client.py +87 -0
- rlmagent/model/transport/faults.py +65 -0
- rlmagent/model/transport/responses_socket.py +223 -0
- rlmagent/model/vertex.py +93 -0
- rlmagent/network.py +123 -0
- rlmagent/packages.py +246 -0
- rlmagent/planning.py +84 -0
- rlmagent/playbook/__init__.py +1 -0
- rlmagent/playbook/apply.py +106 -0
- rlmagent/playbook/digest.py +63 -0
- rlmagent/playbook/improver.py +272 -0
- rlmagent/playbook/plan.py +99 -0
- rlmagent/playbook/store.py +67 -0
- rlmagent/plugins.py +327 -0
- rlmagent/policy.py +105 -0
- rlmagent/prompt_refs.py +57 -0
- rlmagent/prompts.py +331 -0
- rlmagent/prune.py +60 -0
- rlmagent/py.typed +0 -0
- rlmagent/reasoning.py +135 -0
- rlmagent/refinement/__init__.py +1 -0
- rlmagent/refinement/controller.py +448 -0
- rlmagent/refinement/engine.py +220 -0
- rlmagent/refinement/evidence.py +131 -0
- rlmagent/refinement/scoped.py +90 -0
- rlmagent/refinement/store.py +193 -0
- rlmagent/refresh.py +202 -0
- rlmagent/routing.py +52 -0
- rlmagent/runtime.py +575 -0
- rlmagent/safety.py +267 -0
- rlmagent/session_html.py +57 -0
- rlmagent/session_import.py +52 -0
- rlmagent/session_share.py +55 -0
- rlmagent/sessions.py +249 -0
- rlmagent/shell_sandbox.py +88 -0
- rlmagent/skillset.py +320 -0
- rlmagent/steering.py +148 -0
- rlmagent/stops.py +62 -0
- rlmagent/subscriptions.py +219 -0
- rlmagent/tasks.py +98 -0
- rlmagent/toolhost.py +310 -0
- rlmagent/tools/__init__.py +56 -0
- rlmagent/tools/_shared.py +210 -0
- rlmagent/tools/files.py +462 -0
- rlmagent/tools/kernel.py +93 -0
- rlmagent/tools/patch_tool.py +68 -0
- rlmagent/tools/search.py +188 -0
- rlmagent/trust.py +383 -0
- rlmagent/tui/__init__.py +1 -0
- rlmagent/tui/adapter.py +506 -0
- rlmagent/tui/app.py +711 -0
- rlmagent/tui/approval_screen.py +57 -0
- rlmagent/tui/choice_screen.py +47 -0
- rlmagent/tui/completions.py +116 -0
- rlmagent/tui/components.py +25 -0
- rlmagent/tui/composer.py +40 -0
- rlmagent/tui/factory_view.py +86 -0
- rlmagent/tui/inspect.py +193 -0
- rlmagent/tui/messages.py +215 -0
- rlmagent/tui/model_screen.py +307 -0
- rlmagent/tui/prompt.py +98 -0
- rlmagent/tui/question_screen.py +52 -0
- rlmagent/tui/replay.py +141 -0
- rlmagent/tui/rlm_agent.tcss +405 -0
- rlmagent/tui/search_box.py +91 -0
- rlmagent/tui/sidebar.py +182 -0
- rlmagent/tui/status.py +49 -0
- rlmagent/tui/steps.py +283 -0
- rlmagent/tui/suggest.py +778 -0
- rlmagent/tui/transcript.py +172 -0
- rlmagent/tui/trust_dialog.py +58 -0
- rlmagent/tui/worker_attach.py +141 -0
- rlmagent/ui/__init__.py +1 -0
- rlmagent/ui/render.py +211 -0
- rlmagent/undo.py +109 -0
- rlmagent/utils/__init__.py +1 -0
- rlmagent/websearch.py +75 -0
- rlmagent/worker.py +1275 -0
- rlmagent/worker_service.py +203 -0
- rlmness_cli-0.2.0.dist-info/METADATA +882 -0
- rlmness_cli-0.2.0.dist-info/RECORD +203 -0
- rlmness_cli-0.2.0.dist-info/WHEEL +4 -0
- rlmness_cli-0.2.0.dist-info/entry_points.txt +2 -0
- rlmness_cli-0.2.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""Register the fleet watcher with the current user's service manager."""
|
|
2
|
+
|
|
3
|
+
import csv
|
|
4
|
+
import hashlib
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
import tempfile
|
|
10
|
+
from contextlib import redirect_stderr, redirect_stdout
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from xml.etree.ElementTree import Element, SubElement, tostring
|
|
13
|
+
|
|
14
|
+
from rlmagent.discovery import default_paths
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _command(*args: str, check=True) -> subprocess.CompletedProcess:
|
|
18
|
+
try:
|
|
19
|
+
result = subprocess.run(args, capture_output=True, text=True, errors="replace", timeout=30)
|
|
20
|
+
except subprocess.TimeoutExpired as exc:
|
|
21
|
+
raise ValueError(f"{args[0]} timed out") from exc
|
|
22
|
+
if check and result.returncode:
|
|
23
|
+
raise ValueError((result.stderr or result.stdout).strip() or f"{args[0]} failed")
|
|
24
|
+
return result
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _print_result(result: subprocess.CompletedProcess) -> int:
|
|
28
|
+
if result.stdout:
|
|
29
|
+
print(result.stdout, end="")
|
|
30
|
+
if result.stderr:
|
|
31
|
+
print(result.stderr, end="", file=sys.stderr)
|
|
32
|
+
return result.returncode
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _task_xml(name: str, sid: str, argv: list[str]) -> bytes:
|
|
36
|
+
task = Element("Task", {"version": "1.2", "xmlns": "http://schemas.microsoft.com/windows/2004/02/mit/task"})
|
|
37
|
+
registration = SubElement(task, "RegistrationInfo")
|
|
38
|
+
SubElement(registration, "Description").text = "Recover saved RLM Agent workers when they exit."
|
|
39
|
+
SubElement(registration, "URI").text = f"\\{name}"
|
|
40
|
+
trigger = SubElement(SubElement(task, "Triggers"), "LogonTrigger")
|
|
41
|
+
SubElement(trigger, "UserId").text = sid
|
|
42
|
+
principal = SubElement(SubElement(task, "Principals"), "Principal", id="Watcher")
|
|
43
|
+
SubElement(principal, "UserId").text = sid
|
|
44
|
+
SubElement(principal, "LogonType").text = "InteractiveToken"
|
|
45
|
+
SubElement(principal, "RunLevel").text = "LeastPrivilege"
|
|
46
|
+
settings = SubElement(task, "Settings")
|
|
47
|
+
for key, value in (
|
|
48
|
+
("MultipleInstancesPolicy", "IgnoreNew"), ("DisallowStartIfOnBatteries", "false"),
|
|
49
|
+
("StopIfGoingOnBatteries", "false"), ("ExecutionTimeLimit", "PT0S"),
|
|
50
|
+
):
|
|
51
|
+
SubElement(settings, key).text = value
|
|
52
|
+
retry = SubElement(settings, "RestartOnFailure")
|
|
53
|
+
SubElement(retry, "Interval").text = "PT1M"
|
|
54
|
+
SubElement(retry, "Count").text = "10"
|
|
55
|
+
action = SubElement(SubElement(task, "Actions", Context="Watcher"), "Exec")
|
|
56
|
+
SubElement(action, "Command").text = argv[0]
|
|
57
|
+
SubElement(action, "Arguments").text = subprocess.list2cmdline(argv[1:])
|
|
58
|
+
return tostring(task, encoding="utf-16", xml_declaration=True)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _windows(action: str, name: str, argv: list[str], home: Path) -> int:
|
|
62
|
+
name = f"RlmAgentFleet-{name}"
|
|
63
|
+
if action == "install":
|
|
64
|
+
identity = _command("whoami.exe", "/user", "/fo", "csv", "/nh")
|
|
65
|
+
rows = list(csv.reader(identity.stdout.splitlines()))
|
|
66
|
+
sid = rows[0][-1] if rows and rows[0] else ""
|
|
67
|
+
if not re.fullmatch(r"S-1(?:-\d+)+", sid):
|
|
68
|
+
raise ValueError("Could not identify the current Windows user")
|
|
69
|
+
home.mkdir(parents=True, exist_ok=True)
|
|
70
|
+
with tempfile.TemporaryDirectory(dir=home) as directory:
|
|
71
|
+
definition = Path(directory) / "fleet.xml"
|
|
72
|
+
definition.write_bytes(_task_xml(name, sid, argv))
|
|
73
|
+
_command("schtasks.exe", "/Create", "/TN", name, "/XML", str(definition), "/F")
|
|
74
|
+
action = "start"
|
|
75
|
+
if action == "uninstall":
|
|
76
|
+
_command("schtasks.exe", "/End", "/TN", name, check=False)
|
|
77
|
+
native = {"start": "/Run", "stop": "/End", "status": "/Query", "uninstall": "/Delete"}
|
|
78
|
+
extra = {"status": ["/FO", "LIST", "/V"], "uninstall": ["/F"]}.get(action, [])
|
|
79
|
+
return _print_result(_command("schtasks.exe", native[action], "/TN", name, *extra, check=False))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _systemd_unit(argv: list[str]) -> str:
|
|
83
|
+
# systemd expands percent specifiers and dollar variables even in quoted arguments.
|
|
84
|
+
if any(any(ord(char) < 32 for char in arg) for arg in argv):
|
|
85
|
+
raise ValueError("Service paths may not contain control characters")
|
|
86
|
+
quoted = [
|
|
87
|
+
'"' + arg.replace("\\", "\\\\").replace('"', '\\"').replace("%", "%%").replace("$", "$$") + '"'
|
|
88
|
+
for arg in argv
|
|
89
|
+
]
|
|
90
|
+
return (
|
|
91
|
+
"[Unit]\nDescription=RLM Agent worker fleet watcher\n\n"
|
|
92
|
+
"[Service]\nType=exec\nExecStart=" + " ".join(quoted) + "\n"
|
|
93
|
+
"Restart=on-failure\nRestartSec=5s\nKillMode=process\n\n"
|
|
94
|
+
"[Install]\nWantedBy=default.target\n"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _linux(action: str, name: str, argv: list[str]) -> int:
|
|
99
|
+
name = f"rlm-agent-fleet-{name}.service"
|
|
100
|
+
config = Path(os.environ.get("XDG_CONFIG_HOME") or Path.home() / ".config")
|
|
101
|
+
unit = config / "systemd" / "user" / name
|
|
102
|
+
manager = ("systemctl", "--user")
|
|
103
|
+
if action == "install":
|
|
104
|
+
definition = _systemd_unit(argv)
|
|
105
|
+
unit.parent.mkdir(parents=True, exist_ok=True)
|
|
106
|
+
with tempfile.TemporaryDirectory(dir=unit.parent) as directory:
|
|
107
|
+
staged = Path(directory) / unit.name
|
|
108
|
+
staged.write_text(definition, encoding="utf-8")
|
|
109
|
+
staged.replace(unit)
|
|
110
|
+
_command(*manager, "daemon-reload")
|
|
111
|
+
_command(*manager, "enable", name)
|
|
112
|
+
action = "restart"
|
|
113
|
+
if action == "uninstall":
|
|
114
|
+
_command(*manager, "disable", "--now", name)
|
|
115
|
+
unit.unlink(missing_ok=True)
|
|
116
|
+
return _print_result(_command(*manager, "daemon-reload", check=False))
|
|
117
|
+
return _print_result(_command(*manager, action, "--no-pager", name, check=False))
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _launchd_plist(label: str, argv: list[str], home: Path) -> bytes:
|
|
121
|
+
import plistlib
|
|
122
|
+
|
|
123
|
+
log = str(home / "fleet-service.log")
|
|
124
|
+
return plistlib.dumps({
|
|
125
|
+
"Label": label,
|
|
126
|
+
"ProgramArguments": argv,
|
|
127
|
+
"RunAtLoad": True,
|
|
128
|
+
# Restart only after a failure, as the other service managers do.
|
|
129
|
+
"KeepAlive": {"SuccessfulExit": False},
|
|
130
|
+
"ThrottleInterval": 5,
|
|
131
|
+
"ProcessType": "Background",
|
|
132
|
+
"StandardOutPath": log,
|
|
133
|
+
"StandardErrorPath": log,
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _macos(action: str, name: str, argv: list[str], home: Path) -> int:
|
|
138
|
+
label = f"com.rlmagent.fleet.{name}"
|
|
139
|
+
agents = Path.home() / "Library" / "LaunchAgents"
|
|
140
|
+
plist = agents / f"{label}.plist"
|
|
141
|
+
domain = f"gui/{os.getuid()}"
|
|
142
|
+
target = f"{domain}/{label}"
|
|
143
|
+
if action == "install":
|
|
144
|
+
home.mkdir(parents=True, exist_ok=True)
|
|
145
|
+
agents.mkdir(parents=True, exist_ok=True)
|
|
146
|
+
with tempfile.TemporaryDirectory(dir=agents) as directory:
|
|
147
|
+
staged = Path(directory) / plist.name
|
|
148
|
+
staged.write_bytes(_launchd_plist(label, argv, home))
|
|
149
|
+
staged.replace(plist)
|
|
150
|
+
# Reinstalling replaces a loaded definition rather than failing on it.
|
|
151
|
+
_command("launchctl", "bootout", target, check=False)
|
|
152
|
+
_command("launchctl", "bootstrap", domain, str(plist))
|
|
153
|
+
return _print_result(_command("launchctl", "kickstart", "-k", target, check=False))
|
|
154
|
+
if action == "uninstall":
|
|
155
|
+
result = _command("launchctl", "bootout", target, check=False)
|
|
156
|
+
plist.unlink(missing_ok=True)
|
|
157
|
+
return _print_result(result)
|
|
158
|
+
if action == "start":
|
|
159
|
+
if not plist.exists():
|
|
160
|
+
raise ValueError("The worker service is not installed; run worker service install")
|
|
161
|
+
_command("launchctl", "bootstrap", domain, str(plist), check=False)
|
|
162
|
+
return _print_result(_command("launchctl", "kickstart", target, check=False))
|
|
163
|
+
if action == "stop":
|
|
164
|
+
return _print_result(_command("launchctl", "bootout", target, check=False))
|
|
165
|
+
return _print_result(_command("launchctl", "print", target, check=False))
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def manage(action: str) -> int:
|
|
169
|
+
paths = default_paths()
|
|
170
|
+
home = paths.home.resolve()
|
|
171
|
+
name = hashlib.sha256(os.path.normcase(str(home)).encode()).hexdigest()[:16]
|
|
172
|
+
argv = [sys.executable, "-m", "rlmagent.worker_service", str(home), str(paths.sessions.resolve())]
|
|
173
|
+
if sys.platform == "win32":
|
|
174
|
+
windowless = Path(sys.executable).with_name("pythonw.exe")
|
|
175
|
+
if windowless.is_file():
|
|
176
|
+
argv[0] = str(windowless)
|
|
177
|
+
return _windows(action, name, argv, home)
|
|
178
|
+
if sys.platform == "linux":
|
|
179
|
+
return _linux(action, name, argv)
|
|
180
|
+
if sys.platform == "darwin":
|
|
181
|
+
return _macos(action, name, argv, home)
|
|
182
|
+
raise ValueError("Worker service management needs Windows Task Scheduler, systemd or launchd")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _run(home: str, sessions: str) -> int:
|
|
186
|
+
from rlmagent.worker import _watch_fleet
|
|
187
|
+
|
|
188
|
+
os.environ["RLM_AGENT_HOME"] = home
|
|
189
|
+
os.environ["RLM_AGENT_SESSIONS_DIR"] = sessions
|
|
190
|
+
if sys.platform != "win32":
|
|
191
|
+
return _watch_fleet()
|
|
192
|
+
executable = Path(sys.executable)
|
|
193
|
+
console = executable.with_name("python.exe")
|
|
194
|
+
if executable.name.lower() == "pythonw.exe" and console.is_file():
|
|
195
|
+
# Workers keep using the console interpreter with their existing detached flags.
|
|
196
|
+
sys.executable = str(console)
|
|
197
|
+
with (Path(home) / "fleet-service.log").open("a", encoding="utf-8", buffering=1) as log:
|
|
198
|
+
with redirect_stdout(log), redirect_stderr(log):
|
|
199
|
+
return _watch_fleet()
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
if __name__ == "__main__":
|
|
203
|
+
raise SystemExit(_run(*sys.argv[1:]))
|