swarph-cli 0.7.3__tar.gz → 0.7.4__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.
- {swarph_cli-0.7.3/src/swarph_cli.egg-info → swarph_cli-0.7.4}/PKG-INFO +1 -1
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/pyproject.toml +1 -1
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli/__init__.py +1 -1
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli/commands/watchdog.py +44 -2
- {swarph_cli-0.7.3 → swarph_cli-0.7.4/src/swarph_cli.egg-info}/PKG-INFO +1 -1
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/tests/test_watchdog.py +88 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/LICENSE +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/README.md +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/setup.cfg +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli/cell.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli/commands/hook_output.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli/commands/spawn.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli/main.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli.egg-info/SOURCES.txt +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/tests/test_hook_output.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/tests/test_import_command.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/tests/test_main.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.7.3 → swarph_cli-0.7.4}/tests/test_spawn_command.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.4
|
|
4
4
|
Summary: The `swarph` binary — multi-LLM CLI with mesh-gateway integration. v0.7.0 ships Phase 7 substrate-doc R7 §11.1.7 operator-tooling layer in 5 increments: PR-A `--new-instance` flag (sibling-spawn case) + PR-B auto-suffix on collision (sibling-slot persistence) + PR-C SessionStart hook (closes bare-claude operator-paste gap) + watchdog (stranded-session recovery) + PR-D swarph-shared cell.yaml relocation (cell-yaml schema graduates to swarph-shared 0.3.0 kernel-tier; substrate-doc R7 §11.1.5 (O5) RESOLVED).
|
|
5
5
|
Author: Pierre Samson, Claude Opus
|
|
6
6
|
License: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "swarph-cli"
|
|
7
|
-
version = "0.7.
|
|
7
|
+
version = "0.7.4"
|
|
8
8
|
description = "The `swarph` binary — multi-LLM CLI with mesh-gateway integration. v0.7.0 ships Phase 7 substrate-doc R7 §11.1.7 operator-tooling layer in 5 increments: PR-A `--new-instance` flag (sibling-spawn case) + PR-B auto-suffix on collision (sibling-slot persistence) + PR-C SessionStart hook (closes bare-claude operator-paste gap) + watchdog (stranded-session recovery) + PR-D swarph-shared cell.yaml relocation (cell-yaml schema graduates to swarph-shared 0.3.0 kernel-tier; substrate-doc R7 §11.1.5 (O5) RESOLVED)."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -66,6 +66,7 @@ import argparse
|
|
|
66
66
|
import json
|
|
67
67
|
import os
|
|
68
68
|
import shlex
|
|
69
|
+
import shutil
|
|
69
70
|
import subprocess
|
|
70
71
|
import sys
|
|
71
72
|
import time
|
|
@@ -622,6 +623,35 @@ _SYSTEMD_UNIT_DIR = Path("/etc/systemd/system")
|
|
|
622
623
|
_SYSTEMD_DEFAULT_DIR = Path("/etc/default")
|
|
623
624
|
_SYSTEMD_UNIT_NAMES = ("swarph-watchdog.service", "swarph-watchdog.timer")
|
|
624
625
|
_SYSTEMD_DEFAULT_NAME = "swarph-watchdog" # /etc/default/swarph-watchdog
|
|
626
|
+
# v0.7.3 bundled template's ExecStart placeholder — substituted at install time
|
|
627
|
+
# by `_resolve_swarph_bin()` to the actual binary path on the install host.
|
|
628
|
+
# Fixes the v0.7.3 hardcode that broke pipx-installed peers (binary at
|
|
629
|
+
# ~/.local/bin/swarph not /usr/local/bin/swarph).
|
|
630
|
+
_SWARPH_BIN_PLACEHOLDER = "/usr/local/bin/swarph"
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
def _resolve_swarph_bin() -> str:
|
|
634
|
+
"""Resolve the absolute path of the running swarph binary.
|
|
635
|
+
|
|
636
|
+
Resolution order:
|
|
637
|
+
1. ``sys.argv[0]`` if it's an absolute path — most reliable, equals
|
|
638
|
+
the path the user invoked
|
|
639
|
+
2. ``shutil.which(sys.argv[0])`` — bare-name invocation, look up in PATH
|
|
640
|
+
3. ``shutil.which("swarph")`` — generic PATH lookup as fallback
|
|
641
|
+
4. ``/usr/local/bin/swarph`` — last-resort default (matches v0.7.3
|
|
642
|
+
hardcode behavior; no regression if all three above fail)
|
|
643
|
+
|
|
644
|
+
ALWAYS returns an absolute path — systemd ExecStart requires absolute.
|
|
645
|
+
Relative inputs (e.g. ``venv/bin/swarph`` from editable installs) get
|
|
646
|
+
abspath'd against cwd. Never raises.
|
|
647
|
+
"""
|
|
648
|
+
invoked = sys.argv[0] or "swarph"
|
|
649
|
+
if Path(invoked).is_absolute():
|
|
650
|
+
return invoked
|
|
651
|
+
resolved = shutil.which(invoked) or shutil.which("swarph")
|
|
652
|
+
if not resolved:
|
|
653
|
+
return _SWARPH_BIN_PLACEHOLDER
|
|
654
|
+
return os.path.abspath(resolved)
|
|
625
655
|
|
|
626
656
|
|
|
627
657
|
def _bundled_systemd_files() -> dict[str, str]:
|
|
@@ -656,6 +686,18 @@ def run_install_service(args: argparse.Namespace) -> int:
|
|
|
656
686
|
"""
|
|
657
687
|
files = _bundled_systemd_files()
|
|
658
688
|
|
|
689
|
+
# v0.7.4: substitute the bundled service template's ExecStart placeholder
|
|
690
|
+
# with the actual swarph binary path on this host. Fixes the v0.7.3 hardcode
|
|
691
|
+
# that broke pipx-installed peers (binary at ~/.local/bin/swarph not
|
|
692
|
+
# /usr/local/bin/swarph). Pipx is the recommended install path on droplet
|
|
693
|
+
# + lab, so the hardcode bit BOTH peers on first install attempt today.
|
|
694
|
+
swarph_bin = _resolve_swarph_bin()
|
|
695
|
+
service_content = files[_SYSTEMD_UNIT_NAMES[0]].replace(
|
|
696
|
+
f"ExecStart={_SWARPH_BIN_PLACEHOLDER}",
|
|
697
|
+
f"ExecStart={swarph_bin}",
|
|
698
|
+
1,
|
|
699
|
+
)
|
|
700
|
+
|
|
659
701
|
# Template the default file with the requested role
|
|
660
702
|
default_content = files["swarph-watchdog.default"].replace(
|
|
661
703
|
"SWARPH_CELL=lab",
|
|
@@ -664,13 +706,13 @@ def run_install_service(args: argparse.Namespace) -> int:
|
|
|
664
706
|
)
|
|
665
707
|
|
|
666
708
|
targets = [
|
|
667
|
-
(_SYSTEMD_UNIT_DIR / _SYSTEMD_UNIT_NAMES[0],
|
|
709
|
+
(_SYSTEMD_UNIT_DIR / _SYSTEMD_UNIT_NAMES[0], service_content),
|
|
668
710
|
(_SYSTEMD_UNIT_DIR / _SYSTEMD_UNIT_NAMES[1], files[_SYSTEMD_UNIT_NAMES[1]]),
|
|
669
711
|
(_SYSTEMD_DEFAULT_DIR / _SYSTEMD_DEFAULT_NAME, default_content),
|
|
670
712
|
]
|
|
671
713
|
|
|
672
714
|
if args.dry_run:
|
|
673
|
-
print(f"# DRY RUN — cell={args.cell}", file=sys.stderr)
|
|
715
|
+
print(f"# DRY RUN — cell={args.cell} swarph_bin={swarph_bin}", file=sys.stderr)
|
|
674
716
|
for path, content in targets:
|
|
675
717
|
print(f"\n# would write {path}:", file=sys.stderr)
|
|
676
718
|
print(content, file=sys.stderr)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.4
|
|
4
4
|
Summary: The `swarph` binary — multi-LLM CLI with mesh-gateway integration. v0.7.0 ships Phase 7 substrate-doc R7 §11.1.7 operator-tooling layer in 5 increments: PR-A `--new-instance` flag (sibling-spawn case) + PR-B auto-suffix on collision (sibling-slot persistence) + PR-C SessionStart hook (closes bare-claude operator-paste gap) + watchdog (stranded-session recovery) + PR-D swarph-shared cell.yaml relocation (cell-yaml schema graduates to swarph-shared 0.3.0 kernel-tier; substrate-doc R7 §11.1.5 (O5) RESOLVED).
|
|
5
5
|
Author: Pierre Samson, Claude Opus
|
|
6
6
|
License: MIT
|
|
@@ -624,3 +624,91 @@ def test_bundled_systemd_files_readable():
|
|
|
624
624
|
assert "OnUnitActiveSec=5min" in files["swarph-watchdog.timer"]
|
|
625
625
|
# Default file has the SWARPH_CELL=lab template line
|
|
626
626
|
assert "SWARPH_CELL=lab" in files["swarph-watchdog.default"]
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
# ---------------------------------------------------------------------------
|
|
630
|
+
# v0.7.4 — _resolve_swarph_bin + ExecStart templating
|
|
631
|
+
# ---------------------------------------------------------------------------
|
|
632
|
+
#
|
|
633
|
+
# v0.7.3 shipped a hardcoded ExecStart=/usr/local/bin/swarph that broke
|
|
634
|
+
# pipx-installed peers (lab + drop both hit this on first install attempt
|
|
635
|
+
# 2026-05-14). v0.7.4 resolves the path at install time via _resolve_swarph_bin
|
|
636
|
+
# and substitutes into the bundled service template.
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
def test_resolve_swarph_bin_absolute_argv0_wins(monkeypatch):
|
|
640
|
+
"""If sys.argv[0] is absolute, it's the most reliable signal — use it."""
|
|
641
|
+
from swarph_cli.commands.watchdog import _resolve_swarph_bin
|
|
642
|
+
|
|
643
|
+
monkeypatch.setattr("sys.argv", ["/home/ubuntu/.local/bin/swarph", "watchdog"])
|
|
644
|
+
assert _resolve_swarph_bin() == "/home/ubuntu/.local/bin/swarph"
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
def test_resolve_swarph_bin_bare_name_resolved_via_path(monkeypatch, tmp_path):
|
|
648
|
+
"""Bare-name argv[0] resolves via PATH."""
|
|
649
|
+
from swarph_cli.commands.watchdog import _resolve_swarph_bin
|
|
650
|
+
|
|
651
|
+
fake_bin = tmp_path / "swarph"
|
|
652
|
+
fake_bin.write_text("#!/bin/sh\nexit 0\n")
|
|
653
|
+
fake_bin.chmod(0o755)
|
|
654
|
+
monkeypatch.setattr("sys.argv", ["swarph", "watchdog"])
|
|
655
|
+
monkeypatch.setattr("shutil.which", lambda name: str(fake_bin) if name == "swarph" else None)
|
|
656
|
+
assert _resolve_swarph_bin() == str(fake_bin)
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
def test_resolve_swarph_bin_falls_back_to_placeholder_if_unresolvable(monkeypatch):
|
|
660
|
+
"""All resolution paths fail → fall back to /usr/local/bin/swarph
|
|
661
|
+
(v0.7.3 hardcode behavior; no regression vs prior version)."""
|
|
662
|
+
from swarph_cli.commands.watchdog import _resolve_swarph_bin, _SWARPH_BIN_PLACEHOLDER
|
|
663
|
+
|
|
664
|
+
monkeypatch.setattr("sys.argv", ["swarph", "watchdog"])
|
|
665
|
+
monkeypatch.setattr("shutil.which", lambda name: None)
|
|
666
|
+
assert _resolve_swarph_bin() == _SWARPH_BIN_PLACEHOLDER
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
def test_install_service_dry_run_substitutes_swarph_bin(isolated_state, capsys, monkeypatch):
|
|
670
|
+
"""Dry-run preview shows resolved swarph path in ExecStart, not hardcoded
|
|
671
|
+
/usr/local/bin/swarph (when the host actually has swarph elsewhere)."""
|
|
672
|
+
pipx_path = "/home/ubuntu/.local/bin/swarph"
|
|
673
|
+
monkeypatch.setattr("sys.argv", [pipx_path, "watchdog"])
|
|
674
|
+
rc = run_watchdog(argv=["--install-service", "--cell", "droplet", "--dry-run"])
|
|
675
|
+
assert rc == 0
|
|
676
|
+
captured = capsys.readouterr()
|
|
677
|
+
# Header surfaces the resolved binary
|
|
678
|
+
assert f"swarph_bin={pipx_path}" in captured.err
|
|
679
|
+
# Service file's ExecStart now points at pipx path, NOT the placeholder
|
|
680
|
+
assert f"ExecStart={pipx_path} watchdog --check" in captured.err
|
|
681
|
+
# And the v0.7.3-hardcoded path no longer appears in the preview
|
|
682
|
+
assert "ExecStart=/usr/local/bin/swarph watchdog --check" not in captured.err
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
def test_install_service_dry_run_preserves_default_path_when_absolute(isolated_state, capsys, monkeypatch):
|
|
686
|
+
"""When sys.argv[0] IS /usr/local/bin/swarph (the v0.7.3 default path),
|
|
687
|
+
the substitution still happens but produces the same line — no diff vs
|
|
688
|
+
v0.7.3 for this case."""
|
|
689
|
+
monkeypatch.setattr("sys.argv", ["/usr/local/bin/swarph", "watchdog"])
|
|
690
|
+
rc = run_watchdog(argv=["--install-service", "--cell", "lab", "--dry-run"])
|
|
691
|
+
assert rc == 0
|
|
692
|
+
captured = capsys.readouterr()
|
|
693
|
+
assert "ExecStart=/usr/local/bin/swarph watchdog --check" in captured.err
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
def test_resolve_swarph_bin_relative_with_slash_resolves_to_absolute(tmp_path, monkeypatch):
|
|
697
|
+
"""Relative path with slash (e.g. editable install's venv/bin/swarph)
|
|
698
|
+
must be absolutized — systemd ExecStart needs absolute. Regression guard
|
|
699
|
+
for the abspath fix on top of v0.7.4 path-autodetect."""
|
|
700
|
+
from swarph_cli.commands.watchdog import _resolve_swarph_bin
|
|
701
|
+
|
|
702
|
+
fake = tmp_path / "swarph"
|
|
703
|
+
fake.write_text("#!/bin/sh\nexit 0\n")
|
|
704
|
+
fake.chmod(0o755)
|
|
705
|
+
# Simulate `./swarph` from cwd=tmp_path
|
|
706
|
+
monkeypatch.chdir(tmp_path)
|
|
707
|
+
monkeypatch.setattr("sys.argv", ["./swarph", "watchdog"])
|
|
708
|
+
# shutil.which('./swarph') returns the relative path as-is when the
|
|
709
|
+
# input contains a slash. _resolve_swarph_bin must abspath it.
|
|
710
|
+
resolved = _resolve_swarph_bin()
|
|
711
|
+
assert Path(resolved).is_absolute(), (
|
|
712
|
+
f"resolver returned non-absolute path: {resolved!r}"
|
|
713
|
+
)
|
|
714
|
+
assert resolved == str(fake)
|
|
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
|
|
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
|
|
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
|
|
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
|