diff --git a/demo-01-cmcp-in-action/run.py b/demo-01-cmcp-in-action/run.py index 17d2223..fb79276 100644 --- a/demo-01-cmcp-in-action/run.py +++ b/demo-01-cmcp-in-action/run.py @@ -36,7 +36,7 @@ def _find_cmcp() -> str: sys.exit("cmcp not found. Run: pip install cmcp-runtime") -def _wait_for_port(port: int, what: str, timeout: float = 60.0) -> None: +def _wait_for_port(port: int, what: str, process=None, timeout: float = 60.0) -> None: """Block until something is listening on 127.0.0.1:port. A fixed sleep used to be enough; cMCP Runtime now does attestation and audit @@ -45,6 +45,9 @@ def _wait_for_port(port: int, what: str, timeout: float = 60.0) -> None: """ deadline = time.monotonic() + timeout while time.monotonic() < deadline: + if process is not None and process.poll() is not None: + sys.exit(f"{what} exited with code {process.returncode} before listening on :{port}. " + "See the *.log files in this demo folder.") try: with socket.create_connection(("127.0.0.1", port), timeout=1): return @@ -106,7 +109,7 @@ def main() -> None: [sys.executable, str(REPO_ROOT / "server" / "server.py")], stdout=server_log, stderr=server_log, ) - _wait_for_port(9001, "MCP filesystem server") + _wait_for_port(9001, "MCP filesystem server", server) print("-- Starting cMCP Runtime (CMCP_DEV_MODE=1) on :8443 --", flush=True) env = os.environ.copy() @@ -116,7 +119,7 @@ def main() -> None: stdout=cmcp_log, stderr=cmcp_log, cwd=SCRIPT_DIR, env=env, ) - _wait_for_port(8443, "cMCP Runtime") + _wait_for_port(8443, "cMCP Runtime", cmcp_proc) print() subprocess.run([sys.executable, str(SCRIPT_DIR / "call.py")], check=True) diff --git a/demo-02-policy-swap/run.py b/demo-02-policy-swap/run.py index ddeeca2..c4b807a 100644 --- a/demo-02-policy-swap/run.py +++ b/demo-02-policy-swap/run.py @@ -42,7 +42,7 @@ def _find_cmcp() -> str: sys.exit("cmcp not found. Run: pip install cmcp-runtime") -def _wait_for_port(port: int, what: str, timeout: float = 60.0) -> None: +def _wait_for_port(port: int, what: str, process=None, timeout: float = 60.0) -> None: """Block until something is listening on 127.0.0.1:port. A fixed sleep used to be enough; cMCP Runtime now does attestation and audit @@ -51,6 +51,9 @@ def _wait_for_port(port: int, what: str, timeout: float = 60.0) -> None: """ deadline = time.monotonic() + timeout while time.monotonic() < deadline: + if process is not None and process.poll() is not None: + sys.exit(f"{what} exited with code {process.returncode} before listening on :{port}. " + "See the *.log files in this demo folder.") try: with socket.create_connection(("127.0.0.1", port), timeout=1): return @@ -156,7 +159,7 @@ def main() -> None: [sys.executable, str(REPO_ROOT / "server" / "server.py")], stdout=server_log, stderr=server_log, ) - _wait_for_port(9001, "MCP filesystem server") + _wait_for_port(9001, "MCP filesystem server", server) env = os.environ.copy() env["CMCP_DEV_MODE"] = "1" @@ -165,7 +168,7 @@ def main() -> None: stdout=cmcp_log, stderr=cmcp_log, cwd=SCRIPT_DIR, env=env, ) - _wait_for_port(8443, "cMCP Runtime") + _wait_for_port(8443, "cMCP Runtime", cmcp_proc) # Step 3: write_file denied print() diff --git a/demo-04-context-enforcement/run.py b/demo-04-context-enforcement/run.py index 60aadd3..949d462 100644 --- a/demo-04-context-enforcement/run.py +++ b/demo-04-context-enforcement/run.py @@ -36,7 +36,7 @@ def _find_cmcp() -> str: sys.exit("cmcp not found. Run: pip install cmcp-runtime") -def _wait_for_port(port: int, what: str, timeout: float = 60.0) -> None: +def _wait_for_port(port: int, what: str, process=None, timeout: float = 60.0) -> None: """Block until something is listening on 127.0.0.1:port. A fixed sleep used to be enough; cMCP Runtime now does attestation and audit @@ -45,6 +45,9 @@ def _wait_for_port(port: int, what: str, timeout: float = 60.0) -> None: """ deadline = time.monotonic() + timeout while time.monotonic() < deadline: + if process is not None and process.poll() is not None: + sys.exit(f"{what} exited with code {process.returncode} before listening on :{port}. " + "See the *.log files in this demo folder.") try: with socket.create_connection(("127.0.0.1", port), timeout=1): return @@ -106,7 +109,7 @@ def main() -> None: [sys.executable, str(REPO_ROOT / "server" / "server.py")], stdout=server_log, stderr=server_log, ) - _wait_for_port(9001, "MCP filesystem server") + _wait_for_port(9001, "MCP filesystem server", server) print("-- Starting cMCP Runtime (CMCP_DEV_MODE=1) on :8443 --", flush=True) env = os.environ.copy() @@ -116,7 +119,7 @@ def main() -> None: stdout=cmcp_log, stderr=cmcp_log, cwd=SCRIPT_DIR, env=env, ) - _wait_for_port(8443, "cMCP Runtime") + _wait_for_port(8443, "cMCP Runtime", cmcp_proc) print() subprocess.run([sys.executable, str(SCRIPT_DIR / "call.py")], check=True) diff --git a/demo-05-compliance-domain/run.py b/demo-05-compliance-domain/run.py index a9d2988..03021c3 100644 --- a/demo-05-compliance-domain/run.py +++ b/demo-05-compliance-domain/run.py @@ -36,7 +36,7 @@ def _find_cmcp() -> str: sys.exit("cmcp not found. Run: pip install cmcp-runtime") -def _wait_for_port(port: int, what: str, timeout: float = 60.0) -> None: +def _wait_for_port(port: int, what: str, process=None, timeout: float = 60.0) -> None: """Block until something is listening on 127.0.0.1:port. A fixed sleep used to be enough; cMCP Runtime now does attestation and audit @@ -45,6 +45,9 @@ def _wait_for_port(port: int, what: str, timeout: float = 60.0) -> None: """ deadline = time.monotonic() + timeout while time.monotonic() < deadline: + if process is not None and process.poll() is not None: + sys.exit(f"{what} exited with code {process.returncode} before listening on :{port}. " + "See the *.log files in this demo folder.") try: with socket.create_connection(("127.0.0.1", port), timeout=1): return @@ -106,7 +109,7 @@ def main() -> None: [sys.executable, str(REPO_ROOT / "server" / "server.py")], stdout=server_log, stderr=server_log, ) - _wait_for_port(9001, "MCP filesystem server") + _wait_for_port(9001, "MCP filesystem server", server) print("-- Starting cMCP Runtime (CMCP_DEV_MODE=1) on :8443 --", flush=True) env = os.environ.copy() @@ -116,7 +119,7 @@ def main() -> None: stdout=cmcp_log, stderr=cmcp_log, cwd=SCRIPT_DIR, env=env, ) - _wait_for_port(8443, "cMCP Runtime") + _wait_for_port(8443, "cMCP Runtime", cmcp_proc) print() subprocess.run([sys.executable, str(SCRIPT_DIR / "call.py")], check=True) diff --git a/demo-10-model-gateway/run.py b/demo-10-model-gateway/run.py index 421ac71..be54aae 100644 --- a/demo-10-model-gateway/run.py +++ b/demo-10-model-gateway/run.py @@ -18,9 +18,13 @@ PORTS = {"model backend": 9010, "model gateway": 8444, "endpoint": 8500} -def _wait(port, name, timeout=60): +def _wait(port, name, process=None, timeout=60): deadline = time.time() + timeout while time.time() < deadline: + if process is not None and process.poll() is not None: + print(f"{name} exited with code {process.returncode} before listening on :{port}. " + "See the *.log files.", file=sys.stderr) + return False with socket.socket() as s: s.settimeout(0.5) if s.connect_ex(("127.0.0.1", port)) == 0: @@ -66,21 +70,21 @@ def main(): procs.append(subprocess.Popen( [sys.executable, str(HERE / "model_server.py")], stdout=logs["model backend"], stderr=logs["model backend"], env=env, cwd=HERE)) - if not _wait(9010, "model backend"): + if not _wait(9010, "model backend", procs[-1]): return 1 print("-- cMCP gateway on :8444 (CMCP_DEV_MODE=1)") procs.append(subprocess.Popen( [cmcp, "start", "--config", config], stdout=logs["model gateway"], stderr=logs["model gateway"], env=env, cwd=HERE)) - if not _wait(8444, "model gateway"): + if not _wait(8444, "model gateway", procs[-1]): return 1 print("-- OpenAI-compatible endpoint on :8500") procs.append(subprocess.Popen( [sys.executable, str(HERE / "endpoint.py")], stdout=logs["endpoint"], stderr=logs["endpoint"], env=env, cwd=HERE)) - if not _wait(8500, "endpoint"): + if not _wait(8500, "endpoint", procs[-1]): return 1 print() diff --git a/demo.py b/demo.py index 587db63..d9ca8d4 100644 --- a/demo.py +++ b/demo.py @@ -172,7 +172,13 @@ def run(idx, title, script, blurb, pause): except (EOFError, KeyboardInterrupt): print("\nStopped."); sys.exit(0) banner(idx, title, blurb) - rc = subprocess.run([sys.executable, str(ROOT / script)]).returncode + try: + rc = subprocess.run( + [sys.executable, str(ROOT / script)], timeout=180 + ).returncode + except subprocess.TimeoutExpired: + print(_c(f"\n[!] Demo {idx} timed out after 180 seconds.", BOLD)) + return 1 if rc != 0: print(_c(f"\n[!] Demo {idx} exited with code {rc}. See the *.log files in the demo folder.", BOLD)) return rc diff --git a/web-console/run.py b/web-console/run.py index 2151cc8..d62bc97 100644 --- a/web-console/run.py +++ b/web-console/run.py @@ -47,7 +47,7 @@ def _find_cmcp() -> str: sys.exit("cmcp not found. Run: pip install cmcp-runtime httpx") -def _wait_for_port(port: int, what: str, timeout: float = 60.0) -> None: +def _wait_for_port(port: int, what: str, process=None, timeout: float = 60.0) -> None: """Block until something is listening on 127.0.0.1:port. A fixed sleep used to be enough; cMCP Runtime now does attestation and audit @@ -56,6 +56,9 @@ def _wait_for_port(port: int, what: str, timeout: float = 60.0) -> None: """ deadline = time.monotonic() + timeout while time.monotonic() < deadline: + if process is not None and process.poll() is not None: + sys.exit(f"{what} exited with code {process.returncode} before listening on :{port}. " + "See server.log and cmcp.log in this folder.") try: with socket.create_connection(("127.0.0.1", port), timeout=1): return @@ -102,7 +105,7 @@ def main() -> None: procs.append(subprocess.Popen( [sys.executable, str(EXAMPLE / "server" / "mock_mcp_server.py")], stdout=server_log, stderr=server_log)) - _wait_for_port(8080, "EU credit-risk MCP server") + _wait_for_port(8080, "EU credit-risk MCP server", procs[-1]) print("-- cMCP gateway on :8443 (CMCP_DEV_MODE=1)", flush=True) env = os.environ.copy() @@ -110,12 +113,12 @@ def main() -> None: procs.append(subprocess.Popen( [_find_cmcp(), "start", "--config", str(GATEWAY_CFG)], stdout=cmcp_log, stderr=cmcp_log, env=env)) - _wait_for_port(8443, "cMCP gateway") + _wait_for_port(8443, "cMCP gateway", procs[-1]) print(f"-- web console on http://localhost:{PORT}", flush=True) web = subprocess.Popen([sys.executable, str(HERE / "webserver.py")], env=os.environ.copy()) procs.append(web) - _wait_for_port(int(PORT), "web console") + _wait_for_port(int(PORT), "web console", web) url = f"http://localhost:{PORT}" print(f"\nOpen {url} (opening it for you now). Ctrl+C to stop.\n", flush=True)