forgexa-cli 1.2.3__tar.gz → 1.2.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {forgexa_cli-1.2.3 → forgexa_cli-1.2.7}/PKG-INFO +1 -1
- {forgexa_cli-1.2.3 → forgexa_cli-1.2.7}/forgexa_cli/__init__.py +1 -1
- {forgexa_cli-1.2.3 → forgexa_cli-1.2.7}/forgexa_cli/daemon.py +89 -1
- {forgexa_cli-1.2.3 → forgexa_cli-1.2.7}/forgexa_cli.egg-info/PKG-INFO +1 -1
- {forgexa_cli-1.2.3 → forgexa_cli-1.2.7}/pyproject.toml +1 -1
- {forgexa_cli-1.2.3 → forgexa_cli-1.2.7}/README.md +0 -0
- {forgexa_cli-1.2.3 → forgexa_cli-1.2.7}/forgexa_cli/_build_config.py +0 -0
- {forgexa_cli-1.2.3 → forgexa_cli-1.2.7}/forgexa_cli/main.py +0 -0
- {forgexa_cli-1.2.3 → forgexa_cli-1.2.7}/forgexa_cli/py.typed +0 -0
- {forgexa_cli-1.2.3 → forgexa_cli-1.2.7}/forgexa_cli.egg-info/SOURCES.txt +0 -0
- {forgexa_cli-1.2.3 → forgexa_cli-1.2.7}/forgexa_cli.egg-info/dependency_links.txt +0 -0
- {forgexa_cli-1.2.3 → forgexa_cli-1.2.7}/forgexa_cli.egg-info/entry_points.txt +0 -0
- {forgexa_cli-1.2.3 → forgexa_cli-1.2.7}/forgexa_cli.egg-info/requires.txt +0 -0
- {forgexa_cli-1.2.3 → forgexa_cli-1.2.7}/forgexa_cli.egg-info/top_level.txt +0 -0
- {forgexa_cli-1.2.3 → forgexa_cli-1.2.7}/setup.cfg +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""forgexa-cli — Forgexa command-line client."""
|
|
2
|
-
__version__ = "1.2.
|
|
2
|
+
__version__ = "1.2.7"
|
|
@@ -242,6 +242,47 @@ def get_hardware_id() -> str:
|
|
|
242
242
|
return uuid.uuid4().hex[:24]
|
|
243
243
|
|
|
244
244
|
|
|
245
|
+
def get_os_info() -> str:
|
|
246
|
+
"""Return a concise OS/arch summary string, e.g. 'macOS 15.0 arm64', 'Ubuntu 24.04 x86_64'."""
|
|
247
|
+
system = platform.system()
|
|
248
|
+
machine = platform.machine()
|
|
249
|
+
if system == "Darwin":
|
|
250
|
+
# e.g. "macOS 15.0 arm64"
|
|
251
|
+
mac_ver = platform.mac_ver()[0] or platform.release()
|
|
252
|
+
return f"macOS {mac_ver} {machine}"
|
|
253
|
+
elif system == "Linux":
|
|
254
|
+
# Try to get distro name+version from /etc/os-release
|
|
255
|
+
distro = _get_linux_distro()
|
|
256
|
+
if distro:
|
|
257
|
+
return f"{distro} {machine}"
|
|
258
|
+
return f"Linux {platform.release().split('-')[0]} {machine}"
|
|
259
|
+
elif system == "Windows":
|
|
260
|
+
# e.g. "Windows 10.0 AMD64"
|
|
261
|
+
win_ver = platform.version().split('.')[0:2]
|
|
262
|
+
return f"Windows {'.'.join(win_ver)} {machine}"
|
|
263
|
+
else:
|
|
264
|
+
return f"{system} {platform.release()} {machine}"
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _get_linux_distro() -> str:
|
|
268
|
+
"""Parse /etc/os-release to get distro name and version, e.g. 'Ubuntu 24.04'."""
|
|
269
|
+
try:
|
|
270
|
+
with open("/etc/os-release") as f:
|
|
271
|
+
info = {}
|
|
272
|
+
for line in f:
|
|
273
|
+
line = line.strip()
|
|
274
|
+
if "=" in line:
|
|
275
|
+
key, _, val = line.partition("=")
|
|
276
|
+
info[key] = val.strip('"')
|
|
277
|
+
name = info.get("NAME", "")
|
|
278
|
+
version = info.get("VERSION_ID", "")
|
|
279
|
+
if name:
|
|
280
|
+
return f"{name} {version}".strip()
|
|
281
|
+
except OSError:
|
|
282
|
+
pass
|
|
283
|
+
return ""
|
|
284
|
+
|
|
285
|
+
|
|
245
286
|
# ── Data Classes ──
|
|
246
287
|
|
|
247
288
|
|
|
@@ -726,6 +767,35 @@ class WorkspaceManager:
|
|
|
726
767
|
)
|
|
727
768
|
except RuntimeError as exc2:
|
|
728
769
|
logger.warning("Failed to reset to origin/%s: %s", default_branch, exc2)
|
|
770
|
+
else:
|
|
771
|
+
# Non-fresh-start (design/coding/testing): ensure working tree
|
|
772
|
+
# is on the correct branch and has the latest commits from remote.
|
|
773
|
+
# This is critical for cross-machine execution where a previous
|
|
774
|
+
# node on another daemon pushed commits to the branch.
|
|
775
|
+
logger.info("Syncing worktree %s to latest origin/%s", ws_path, branch_name)
|
|
776
|
+
try:
|
|
777
|
+
await self._git("checkout", branch_name, cwd=ws_path)
|
|
778
|
+
except RuntimeError:
|
|
779
|
+
# Branch might not exist locally yet — create tracking branch
|
|
780
|
+
try:
|
|
781
|
+
await self._git(
|
|
782
|
+
"checkout", "-B", branch_name, f"origin/{branch_name}",
|
|
783
|
+
cwd=ws_path,
|
|
784
|
+
)
|
|
785
|
+
except RuntimeError as exc:
|
|
786
|
+
logger.warning("Failed to checkout %s: %s", branch_name, exc)
|
|
787
|
+
# Reset working tree to match remote branch (fast-forward)
|
|
788
|
+
try:
|
|
789
|
+
await self._git(
|
|
790
|
+
"reset", "--hard", f"origin/{branch_name}",
|
|
791
|
+
cwd=ws_path,
|
|
792
|
+
)
|
|
793
|
+
except RuntimeError as exc:
|
|
794
|
+
# Remote branch might not exist yet (first node in graph)
|
|
795
|
+
logger.debug(
|
|
796
|
+
"Could not reset to origin/%s: %s — branch may not exist on remote yet",
|
|
797
|
+
branch_name, exc,
|
|
798
|
+
)
|
|
729
799
|
return ws_path
|
|
730
800
|
|
|
731
801
|
# Ensure _main repo is present and up-to-date
|
|
@@ -1043,7 +1113,22 @@ class ProcessManager:
|
|
|
1043
1113
|
if not proc.stdout:
|
|
1044
1114
|
return
|
|
1045
1115
|
while True:
|
|
1046
|
-
|
|
1116
|
+
try:
|
|
1117
|
+
line_bytes = await proc.stdout.readline()
|
|
1118
|
+
except ValueError:
|
|
1119
|
+
# Line exceeded stream buffer limit – fall back to reading
|
|
1120
|
+
# remaining data in bulk to avoid losing output.
|
|
1121
|
+
remaining = await proc.stdout.read()
|
|
1122
|
+
if remaining:
|
|
1123
|
+
for chunk_line in remaining.decode(errors="replace").split("\n"):
|
|
1124
|
+
if chunk_line:
|
|
1125
|
+
stdout_lines.append(chunk_line)
|
|
1126
|
+
if on_chunk:
|
|
1127
|
+
try:
|
|
1128
|
+
await on_chunk([chunk_line])
|
|
1129
|
+
except Exception:
|
|
1130
|
+
pass
|
|
1131
|
+
break
|
|
1047
1132
|
if not line_bytes:
|
|
1048
1133
|
break
|
|
1049
1134
|
line = line_bytes.decode(errors="replace").rstrip("\n")
|
|
@@ -1107,6 +1192,7 @@ class ProcessManager:
|
|
|
1107
1192
|
stdin=asyncio.subprocess.PIPE,
|
|
1108
1193
|
cwd=str(cwd),
|
|
1109
1194
|
env=env,
|
|
1195
|
+
limit=10 * 1024 * 1024, # 10MB line buffer for large JSON output
|
|
1110
1196
|
)
|
|
1111
1197
|
self.active_processes[task_id] = proc
|
|
1112
1198
|
stdout, stderr, returncode = await self._stream_process(
|
|
@@ -1745,6 +1831,7 @@ class HeartbeatService:
|
|
|
1745
1831
|
"active_tasks": self._active_tasks,
|
|
1746
1832
|
"available_agents": self._agents,
|
|
1747
1833
|
"system_metrics": self._collect_system_metrics(),
|
|
1834
|
+
"os_info": get_os_info(),
|
|
1748
1835
|
},
|
|
1749
1836
|
timeout=10,
|
|
1750
1837
|
)
|
|
@@ -1925,6 +2012,7 @@ class ServerConnection:
|
|
|
1925
2012
|
"daemon_id": self.daemon_id,
|
|
1926
2013
|
"hardware_id": self.hardware_id,
|
|
1927
2014
|
"device_name": platform.node(),
|
|
2015
|
+
"os_info": get_os_info(),
|
|
1928
2016
|
"available_agents": agent_dicts,
|
|
1929
2017
|
"max_concurrent_tasks": max_concurrent,
|
|
1930
2018
|
"capabilities": {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|