TTY-repair tranche: bash.ts v2 replaces bare execSync with a process-group-owned spawn wrapper (MODE A: captured pipes, own pgid, SIGTERM->SIGKILL group kill on timeout, listener teardown before finalize) and an explicit detached mode (MODE B: setsid, stdin /dev/null, output to a durable log file, unref) for nohup/setsid/&- suffixed commands. Wait-intent compilation preserved with a STRICT gate (normalized=true + ContinuePendingOperation + concrete operation bound); arbitrary shell syntax passes through untouched. execFile with argv array (no nested sh -c quoting) for the router call. await-operation.ts pinned as canonical source (accepted healthy path, unchanged). scripts/install_mimo_tools.py installs + hash-verifies. tests/test_mimo_tty_contract.py: 8 tests (A-I incl. fd-no-pts, group-kill, no-accumulation, quoting, wait-intent boundary, await-op preservation).
53 lines
1.6 KiB
Python
53 lines
1.6 KiB
Python
#!/usr/bin/env python3
|
|
"""install_mimo_tools.py — canonical MiMo tool installer (TTY-repair tranche).
|
|
|
|
Canonical sources: config/mimo-tools/{bash.ts,await-operation.ts}
|
|
Targets: ~/.mimocode/tools/{bash.ts,await-operation.ts}
|
|
Verifies hashes after install; detects drift in both directions
|
|
(the parity test reads these hashes).
|
|
"""
|
|
import hashlib
|
|
import shutil
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
REPO = Path(__file__).resolve().parent.parent
|
|
TOOLS = ["bash.ts", "await-operation.ts"]
|
|
|
|
|
|
def sha256(p: Path) -> str:
|
|
return hashlib.sha256(p.read_bytes()).hexdigest()
|
|
|
|
|
|
def main() -> int:
|
|
rc = 0
|
|
for name in TOOLS:
|
|
canon = REPO / "config" / "mimo-tools" / name
|
|
target = Path.home() / ".mimocode" / "tools" / name
|
|
if not canon.is_file():
|
|
print(f"{name}: canonical source missing: {canon}")
|
|
rc = 1
|
|
continue
|
|
canon_hash = sha256(canon)
|
|
if target.is_file() and sha256(target) == canon_hash:
|
|
print(f"{name}: in-sync {canon_hash[:12]}")
|
|
continue
|
|
drift = f"DRIFT canonical={canon_hash[:12]}"
|
|
if target.is_file():
|
|
drift += f" installed={sha256(target)[:12]}"
|
|
else:
|
|
drift += " installed=MISSING"
|
|
print(f"{name}: {drift} — installing")
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copy2(canon, target)
|
|
if sha256(target) != canon_hash:
|
|
print(f"{name}: INSTALL FAILED (hash mismatch after copy)")
|
|
rc = 1
|
|
else:
|
|
print(f"{name}: installed {canon_hash[:12]}")
|
|
return rc
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|