diff --git a/agent/anthropic_adapter.py b/agent/anthropic_adapter.py index bf3f4aef859..cf22c2ab8a9 100644 --- a/agent/anthropic_adapter.py +++ b/agent/anthropic_adapter.py @@ -821,6 +821,7 @@ def _read_claude_code_credentials_from_keychain() -> Optional[Dict[str, Any]]: capture_output=True, text=True, timeout=5, + stdin=subprocess.DEVNULL, ) except (OSError, subprocess.TimeoutExpired): logger.debug("Keychain: security command not available or timed out") @@ -1165,7 +1166,7 @@ def run_oauth_setup_token() -> Optional[str]: # Run interactively — stdin/stdout/stderr inherited so user can interact try: - subprocess.run([claude_path, "setup-token"]) + subprocess.run([claude_path, "setup-token"], stdin=subprocess.DEVNULL) except (KeyboardInterrupt, EOFError): return None diff --git a/agent/context_references.py b/agent/context_references.py index 50a33a1d757..dc03ccd740b 100644 --- a/agent/context_references.py +++ b/agent/context_references.py @@ -290,6 +290,7 @@ def _expand_git_reference( capture_output=True, text=True, timeout=30, + stdin=subprocess.DEVNULL, ) except subprocess.TimeoutExpired: return f"{ref.raw}: git command timed out (30s)", None @@ -482,6 +483,7 @@ def _rg_files(path: Path, cwd: Path, limit: int) -> list[Path] | None: capture_output=True, text=True, timeout=10, + stdin=subprocess.DEVNULL, ) except (FileNotFoundError, OSError, subprocess.TimeoutExpired): return None diff --git a/agent/lsp/install.py b/agent/lsp/install.py index 9193b0375c0..418cc510c70 100644 --- a/agent/lsp/install.py +++ b/agent/lsp/install.py @@ -262,6 +262,7 @@ def _install_npm( capture_output=True, text=True, timeout=300, + stdin=subprocess.DEVNULL, ) if proc.returncode != 0: logger.warning( @@ -310,6 +311,7 @@ def _install_go(pkg: str, bin_name: str) -> Optional[str]: text=True, timeout=600, env=env, + stdin=subprocess.DEVNULL, ) if proc.returncode != 0: logger.warning( @@ -347,6 +349,7 @@ def _install_pip(pkg: str, bin_name: str) -> Optional[str]: capture_output=True, text=True, timeout=300, + stdin=subprocess.DEVNULL, ) if proc.returncode != 0: logger.warning( diff --git a/agent/secret_sources/bitwarden.py b/agent/secret_sources/bitwarden.py index 256e3294a10..e025a0ca9b4 100644 --- a/agent/secret_sources/bitwarden.py +++ b/agent/secret_sources/bitwarden.py @@ -274,6 +274,7 @@ def _platform_asset_name() -> str: capture_output=True, text=True, timeout=2, + stdin=subprocess.DEVNULL, ) if "musl" in (res.stdout + res.stderr).lower(): libc = "musl" @@ -525,6 +526,7 @@ def _run_bws_list( capture_output=True, text=True, timeout=_BWS_RUN_TIMEOUT, + stdin=subprocess.DEVNULL, ) except subprocess.TimeoutExpired as exc: raise RuntimeError( diff --git a/agent/skill_preprocessing.py b/agent/skill_preprocessing.py index 2f8015c4435..a7f526b25e7 100644 --- a/agent/skill_preprocessing.py +++ b/agent/skill_preprocessing.py @@ -74,6 +74,7 @@ def run_inline_shell(command: str, cwd: Path | None, timeout: int) -> str: text=True, timeout=max(1, int(timeout)), check=False, + stdin=subprocess.DEVNULL, ) except subprocess.TimeoutExpired: return f"[inline-shell timeout after {timeout}s: {command}]" diff --git a/agent/transports/codex_app_server.py b/agent/transports/codex_app_server.py index be348a6960f..dff16e971da 100644 --- a/agent/transports/codex_app_server.py +++ b/agent/transports/codex_app_server.py @@ -378,6 +378,7 @@ def check_codex_binary( capture_output=True, text=True, timeout=10, + stdin=subprocess.DEVNULL, ) except FileNotFoundError: return False, ( diff --git a/plugins/google_meet/audio_bridge.py b/plugins/google_meet/audio_bridge.py index 11fdd3ff85e..9f13aebb4c6 100644 --- a/plugins/google_meet/audio_bridge.py +++ b/plugins/google_meet/audio_bridge.py @@ -86,6 +86,7 @@ class AudioBridge: ["pactl", "unload-module", str(mod_id)], check=False, capture_output=True, + stdin=subprocess.DEVNULL, ) except Exception: # Best-effort teardown — never raise from here. @@ -111,6 +112,7 @@ class AudioBridge: check=True, capture_output=True, text=True, + stdin=subprocess.DEVNULL, ) except FileNotFoundError as exc: raise RuntimeError( @@ -135,6 +137,7 @@ class AudioBridge: check=True, capture_output=True, text=True, + stdin=subprocess.DEVNULL, ) except subprocess.CalledProcessError as exc: # Roll back the null-sink we just created so we don't leak it. @@ -142,6 +145,7 @@ class AudioBridge: ["pactl", "unload-module", str(sink_mod_id)], check=False, capture_output=True, + stdin=subprocess.DEVNULL, ) raise RuntimeError( f"pactl load-module virtual-source failed: {exc.stderr or exc}" diff --git a/plugins/memory/byterover/__init__.py b/plugins/memory/byterover/__init__.py index eafd9b2cfe5..82f3a6daf62 100644 --- a/plugins/memory/byterover/__init__.py +++ b/plugins/memory/byterover/__init__.py @@ -94,6 +94,7 @@ def _run_brv(args: List[str], timeout: int = _QUERY_TIMEOUT, result = subprocess.run( cmd, capture_output=True, text=True, timeout=timeout, cwd=effective_cwd, env=env, + stdin=subprocess.DEVNULL, ) stdout = result.stdout.strip() stderr = result.stderr.strip() diff --git a/plugins/memory/hindsight/__init__.py b/plugins/memory/hindsight/__init__.py index 53f422b2d7c..dd16f44920e 100644 --- a/plugins/memory/hindsight/__init__.py +++ b/plugins/memory/hindsight/__init__.py @@ -695,6 +695,7 @@ class HindsightMemoryProvider(MemoryProvider): subprocess.run( [uv_path, "pip", "install", "--python", sys.executable, "--quiet", "--upgrade"] + deps_to_install, check=True, timeout=120, capture_output=True, + stdin=subprocess.DEVNULL, ) print(" ✓ Dependencies up to date") except Exception as e: @@ -1101,6 +1102,7 @@ class HindsightMemoryProvider(MemoryProvider): [uv_path, "pip", "install", "--python", sys.executable, "--quiet", "--upgrade", f"hindsight-client>={_MIN_CLIENT_VERSION}"], check=True, timeout=120, capture_output=True, + stdin=subprocess.DEVNULL, ) logger.info("hindsight-client upgraded to >=%s", _MIN_CLIENT_VERSION) except Exception as e: diff --git a/plugins/memory/honcho/cli.py b/plugins/memory/honcho/cli.py index 16f81ef88e9..092b7c823d3 100644 --- a/plugins/memory/honcho/cli.py +++ b/plugins/memory/honcho/cli.py @@ -416,6 +416,7 @@ def _ensure_sdk_installed() -> bool: [sys.executable, "-m", "pip", "install", "honcho-ai>=2.0.1"], capture_output=True, text=True, + stdin=subprocess.DEVNULL, ) if result.returncode == 0: print(" Installed.\n") diff --git a/plugins/memory/honcho/client.py b/plugins/memory/honcho/client.py index cdea97ce37a..df8c839aa81 100644 --- a/plugins/memory/honcho/client.py +++ b/plugins/memory/honcho/client.py @@ -628,6 +628,7 @@ class HonchoClientConfig: root = subprocess.run( ["git", "rev-parse", "--show-toplevel"], capture_output=True, text=True, cwd=cwd, timeout=5, + stdin=subprocess.DEVNULL, ) if root.returncode == 0: return Path(root.stdout.strip()).name diff --git a/plugins/platforms/discord/adapter.py b/plugins/platforms/discord/adapter.py index 1cf33020e7b..450c9767c8f 100644 --- a/plugins/platforms/discord/adapter.py +++ b/plugins/platforms/discord/adapter.py @@ -520,6 +520,7 @@ class VoiceReceiver: ], check=True, timeout=10, + stdin=subprocess.DEVNULL, ) finally: try: diff --git a/plugins/platforms/discord/voice_mixer.py b/plugins/platforms/discord/voice_mixer.py index c8f5b7eef3a..a01fd827243 100644 --- a/plugins/platforms/discord/voice_mixer.py +++ b/plugins/platforms/discord/voice_mixer.py @@ -320,6 +320,7 @@ def decode_to_pcm(path: str, *, timeout: float = 30.0) -> Optional[bytes]: ], capture_output=True, timeout=timeout, + stdin=subprocess.DEVNULL, ) except (subprocess.TimeoutExpired, FileNotFoundError, OSError) as e: logger.warning("decode_to_pcm failed for %s: %s", path, e) diff --git a/tools/checkpoint_manager.py b/tools/checkpoint_manager.py index e4f48f80da1..f0b47734cea 100644 --- a/tools/checkpoint_manager.py +++ b/tools/checkpoint_manager.py @@ -307,6 +307,7 @@ def _run_git( timeout=timeout, env=env, cwd=str(normalized_working_dir), + stdin=subprocess.DEVNULL, ) ok = result.returncode == 0 stdout = result.stdout.strip() @@ -426,6 +427,7 @@ def _init_store(store: Path, working_dir: str) -> Optional[str]: ["git", "init", "--bare", str(store)], capture_output=True, text=True, env=init_env, timeout=_GIT_TIMEOUT, + stdin=subprocess.DEVNULL, ) if result.returncode != 0: return f"Shadow store init failed: {result.stderr.strip()}" diff --git a/tools/code_execution_tool.py b/tools/code_execution_tool.py index 40581e57f2d..76deef2e5c1 100644 --- a/tools/code_execution_tool.py +++ b/tools/code_execution_tool.py @@ -1618,6 +1618,7 @@ def _is_usable_python(python_path: str) -> bool: timeout=5, capture_output=True, creationflags=subprocess.CREATE_NO_WINDOW if _IS_WINDOWS else 0, + stdin=subprocess.DEVNULL, ) return result.returncode == 0 except (OSError, subprocess.TimeoutExpired, subprocess.SubprocessError): diff --git a/tools/env_probe.py b/tools/env_probe.py index dfb715a9871..4b156b06edb 100644 --- a/tools/env_probe.py +++ b/tools/env_probe.py @@ -65,6 +65,7 @@ def _run(cmd: list[str], timeout: float = 3.0) -> tuple[int, str, str]: text=True, timeout=timeout, check=False, + stdin=subprocess.DEVNULL, ) return result.returncode, (result.stdout or "").strip(), (result.stderr or "").strip() except FileNotFoundError: diff --git a/tools/environments/docker.py b/tools/environments/docker.py index 421eb71be80..8245d6879bf 100644 --- a/tools/environments/docker.py +++ b/tools/environments/docker.py @@ -177,6 +177,7 @@ def reap_orphan_containers( listing = subprocess.run( [docker, "ps", "-a", *filters, "--format", "{{.ID}}"], capture_output=True, text=True, timeout=15, check=False, + stdin=subprocess.DEVNULL, ) except (subprocess.TimeoutExpired, OSError) as e: logger.debug("orphan reaper docker ps failed: %s", e) @@ -210,6 +211,7 @@ def reap_orphan_containers( result = subprocess.run( [docker, "rm", "-f", cid], capture_output=True, text=True, timeout=30, + stdin=subprocess.DEVNULL, ) if result.returncode == 0: removed += 1 @@ -239,6 +241,7 @@ def _container_finished_at(docker_exe: str, container_id: str): result = subprocess.run( [docker_exe, "inspect", "--format", "{{.State.FinishedAt}}", container_id], capture_output=True, text=True, timeout=10, check=False, + stdin=subprocess.DEVNULL, ) except (subprocess.TimeoutExpired, OSError) as e: logger.debug("orphan reaper docker inspect %s failed: %s", container_id[:12], e) @@ -381,6 +384,7 @@ def _image_uses_init_entrypoint(docker_exe: str, image: str) -> bool: capture_output=True, text=True, timeout=15, + stdin=subprocess.DEVNULL, ) except (subprocess.SubprocessError, OSError) as e: logger.debug("Docker: could not inspect entrypoint for %s: %s", image, e) @@ -453,6 +457,7 @@ def _ensure_docker_available() -> None: capture_output=True, text=True, timeout=5, + stdin=subprocess.DEVNULL, ) except FileNotFoundError: logger.error( @@ -833,6 +838,7 @@ class DockerEnvironment(BaseEnvironment): text=True, timeout=30, check=True, + stdin=subprocess.DEVNULL, ) except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e: logger.warning( @@ -871,6 +877,7 @@ class DockerEnvironment(BaseEnvironment): text=True, timeout=120, # image pull may take a while check=True, + stdin=subprocess.DEVNULL, ) except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e: # Docker may create the container object before `docker run` @@ -887,6 +894,7 @@ class DockerEnvironment(BaseEnvironment): subprocess.run( [self._docker_exe, "rm", "-f", container_name], capture_output=True, timeout=10, + stdin=subprocess.DEVNULL, ) raise self._container_id = result.stdout.strip() @@ -997,6 +1005,7 @@ class DockerEnvironment(BaseEnvironment): subprocess.run( [self._docker_exe, "start", cid], capture_output=True, text=True, timeout=30, check=True, + stdin=subprocess.DEVNULL, ) self._container_id = cid logger.info("Recovery: restarted container %s", cid[:12]) @@ -1027,6 +1036,7 @@ class DockerEnvironment(BaseEnvironment): ] result = subprocess.run( run_cmd, capture_output=True, text=True, timeout=120, check=True, + stdin=subprocess.DEVNULL, ) self._container_id = result.stdout.strip() self._container_name = new_name @@ -1081,6 +1091,7 @@ class DockerEnvironment(BaseEnvironment): result = subprocess.run( [docker, "info", "--format", "{{.Driver}}"], capture_output=True, text=True, timeout=10, + stdin=subprocess.DEVNULL, ) driver = result.stdout.strip().lower() if driver != "overlay2": @@ -1091,13 +1102,15 @@ class DockerEnvironment(BaseEnvironment): probe = subprocess.run( [docker, "create", "--storage-opt", "size=1m", "hello-world"], capture_output=True, text=True, timeout=15, + stdin=subprocess.DEVNULL, ) if probe.returncode == 0: # Clean up the created container container_id = probe.stdout.strip() if container_id: subprocess.run([docker, "rm", container_id], - capture_output=True, timeout=5) + capture_output=True, timeout=5, + stdin=subprocess.DEVNULL) _storage_opt_ok = True else: _storage_opt_ok = False @@ -1132,6 +1145,7 @@ class DockerEnvironment(BaseEnvironment): text=True, timeout=10, check=False, + stdin=subprocess.DEVNULL, ) except (subprocess.TimeoutExpired, OSError) as e: logger.debug("docker ps probe failed: %s — will start a fresh container", e) @@ -1248,6 +1262,7 @@ class DockerEnvironment(BaseEnvironment): subprocess.run( [docker_exe, "stop", "-t", "10", container_id], capture_output=True, timeout=30, + stdin=subprocess.DEVNULL, ) except (subprocess.TimeoutExpired, OSError) as e: logger.warning("docker stop %s timed out / failed: %s", log_id, e) @@ -1256,6 +1271,7 @@ class DockerEnvironment(BaseEnvironment): subprocess.run( [docker_exe, "rm", "-f", container_id], capture_output=True, timeout=30, + stdin=subprocess.DEVNULL, ) except (subprocess.TimeoutExpired, OSError) as e: logger.warning("docker rm -f %s failed: %s", log_id, e) diff --git a/tools/environments/singularity.py b/tools/environments/singularity.py index 16d1013fed8..666d908b256 100644 --- a/tools/environments/singularity.py +++ b/tools/environments/singularity.py @@ -46,6 +46,7 @@ def _ensure_singularity_available() -> str: try: result = subprocess.run( [exe, "version"], capture_output=True, text=True, timeout=10, + stdin=subprocess.DEVNULL, ) except FileNotFoundError: raise RuntimeError( @@ -136,6 +137,7 @@ def _get_or_build_sif(image: str, executable: str = "apptainer") -> str: result = subprocess.run( [executable, "build", str(sif_path), image], capture_output=True, text=True, timeout=600, env=env, + stdin=subprocess.DEVNULL, ) if result.returncode != 0: logger.warning("SIF build failed, falling back to docker:// URL") @@ -218,7 +220,7 @@ class SingularityEnvironment(BaseEnvironment): cmd.extend([str(self.image), self.instance_id]) try: - result = subprocess.run(cmd, capture_output=True, text=True, timeout=120) + result = subprocess.run(cmd, capture_output=True, text=True, timeout=120, stdin=subprocess.DEVNULL) if result.returncode != 0: raise RuntimeError(f"Failed to start instance: {result.stderr}") self._instance_started = True @@ -250,6 +252,7 @@ class SingularityEnvironment(BaseEnvironment): subprocess.run( [self.executable, "instance", "stop", self.instance_id], capture_output=True, text=True, timeout=30, + stdin=subprocess.DEVNULL, ) logger.info("Singularity instance %s stopped", self.instance_id) except Exception as e: diff --git a/tools/lazy_deps.py b/tools/lazy_deps.py index 3aeace33b70..e4b0a9a57f0 100644 --- a/tools/lazy_deps.py +++ b/tools/lazy_deps.py @@ -365,6 +365,7 @@ def _venv_pip_install(specs: tuple[str, ...], *, timeout: int = 300) -> _Install r = subprocess.run( [uv_bin, "pip", "install", *specs], capture_output=True, text=True, timeout=timeout, env=uv_env, + stdin=subprocess.DEVNULL, ) if r.returncode == 0: return _InstallResult(True, r.stdout or "", r.stderr or "") @@ -378,6 +379,7 @@ def _venv_pip_install(specs: tuple[str, ...], *, timeout: int = 300) -> _Install probe = subprocess.run( pip_cmd + ["--version"], capture_output=True, text=True, timeout=15, + stdin=subprocess.DEVNULL, ) if probe.returncode != 0: raise FileNotFoundError("pip not in venv") @@ -386,6 +388,7 @@ def _venv_pip_install(specs: tuple[str, ...], *, timeout: int = 300) -> _Install subprocess.run( [sys.executable, "-m", "ensurepip", "--upgrade", "--default-pip"], capture_output=True, text=True, timeout=120, check=True, + stdin=subprocess.DEVNULL, ) except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e: return _InstallResult(False, "", @@ -395,6 +398,7 @@ def _venv_pip_install(specs: tuple[str, ...], *, timeout: int = 300) -> _Install r = subprocess.run( pip_cmd + ["install", *specs], capture_output=True, text=True, timeout=timeout, + stdin=subprocess.DEVNULL, ) return _InstallResult(r.returncode == 0, r.stdout or "", r.stderr or "") except subprocess.TimeoutExpired as e: diff --git a/tools/process_registry.py b/tools/process_registry.py index 86970c0fd29..59fcf4a4462 100644 --- a/tools/process_registry.py +++ b/tools/process_registry.py @@ -472,6 +472,7 @@ class ProcessRegistry: text=True, timeout=10, creationflags=windows_hide_flags(), + stdin=subprocess.DEVNULL, ) except (FileNotFoundError, subprocess.TimeoutExpired, OSError): try: diff --git a/tools/skills_hub.py b/tools/skills_hub.py index ca40bb55c05..31a88c973fb 100644 --- a/tools/skills_hub.py +++ b/tools/skills_hub.py @@ -301,6 +301,7 @@ class GitHubAuth: result = subprocess.run( ["gh", "auth", "token"], capture_output=True, text=True, timeout=5, + stdin=subprocess.DEVNULL, ) if result.returncode == 0 and result.stdout.strip(): return result.stdout.strip() diff --git a/tools/terminal_tool.py b/tools/terminal_tool.py index 3e17d2c865e..e859cf10cad 100644 --- a/tools/terminal_tool.py +++ b/tools/terminal_tool.py @@ -2436,13 +2436,13 @@ def check_terminal_requirements() -> bool: if not docker: logger.error("Docker executable not found in PATH or common install locations") return False - result = subprocess.run([docker, "version"], capture_output=True, timeout=5) + result = subprocess.run([docker, "version"], capture_output=True, timeout=5, stdin=subprocess.DEVNULL) return result.returncode == 0 elif env_type == "singularity": executable = shutil.which("apptainer") or shutil.which("singularity") if executable: - result = subprocess.run([executable, "--version"], capture_output=True, timeout=5) + result = subprocess.run([executable, "--version"], capture_output=True, timeout=5, stdin=subprocess.DEVNULL) return result.returncode == 0 return False diff --git a/tools/tirith_security.py b/tools/tirith_security.py index f40da60e52d..757ff5c4503 100644 --- a/tools/tirith_security.py +++ b/tools/tirith_security.py @@ -288,6 +288,7 @@ def _verify_cosign(checksums_path: str, sig_path: str, cert_path: str) -> bool | capture_output=True, text=True, timeout=15, + stdin=subprocess.DEVNULL, ) if result.returncode == 0: logger.info("cosign provenance verification passed") @@ -734,6 +735,7 @@ def check_command_security(command: str) -> dict: capture_output=True, text=True, timeout=timeout, + stdin=subprocess.DEVNULL, ) except OSError as exc: # Covers FileNotFoundError, PermissionError, exec format error. diff --git a/tools/transcription_tools.py b/tools/transcription_tools.py index 449425b0bdb..d0712c81e1e 100644 --- a/tools/transcription_tools.py +++ b/tools/transcription_tools.py @@ -490,6 +490,7 @@ def _terminate_command_stt_process_tree(proc: subprocess.Popen) -> None: stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=5, + stdin=subprocess.DEVNULL, ) except Exception: proc.kill() @@ -555,7 +556,7 @@ def _run_command_stt(command: str, timeout: float) -> subprocess.CompletedProces else: popen_kwargs["start_new_session"] = True - proc = subprocess.Popen(command, **popen_kwargs) + proc = subprocess.Popen(command, **popen_kwargs, stdin=subprocess.DEVNULL) try: stdout, stderr = proc.communicate(timeout=timeout) except subprocess.TimeoutExpired as exc: @@ -1186,7 +1187,7 @@ def _prepare_local_audio(file_path: str, work_dir: str) -> tuple[Optional[str], command = [ffmpeg, "-y", "-i", file_path, converted_path] try: - subprocess.run(command, check=True, capture_output=True, text=True, timeout=300) + subprocess.run(command, check=True, capture_output=True, text=True, timeout=300, stdin=subprocess.DEVNULL) return converted_path, None except subprocess.TimeoutExpired: logger.error("ffmpeg conversion timed out for %s", file_path) @@ -1232,9 +1233,9 @@ def _transcribe_local_command(file_path: str, model_name: str) -> Dict[str, Any] # User-provided templates (env var) may contain shell syntax; auto-detected commands are safe for list mode. use_shell = bool(os.getenv(LOCAL_STT_COMMAND_ENV, "").strip()) if use_shell: - subprocess.run(command, shell=True, check=True, capture_output=True, text=True, timeout=300) + subprocess.run(command, shell=True, check=True, capture_output=True, text=True, timeout=300, stdin=subprocess.DEVNULL) else: - subprocess.run(shlex.split(command), check=True, capture_output=True, text=True, timeout=300) + subprocess.run(shlex.split(command), check=True, capture_output=True, text=True, timeout=300, stdin=subprocess.DEVNULL) txt_files = sorted(Path(output_dir).glob("*.txt")) diff --git a/tools/tts_tool.py b/tools/tts_tool.py index e1d20df3fdb..4d43eef2122 100644 --- a/tools/tts_tool.py +++ b/tools/tts_tool.py @@ -693,6 +693,7 @@ def _terminate_command_tts_process_tree(proc: subprocess.Popen) -> None: stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=5, + stdin=subprocess.DEVNULL, ) except Exception: proc.kill() @@ -745,7 +746,7 @@ def _run_command_tts(command: str, timeout: float) -> subprocess.CompletedProces else: popen_kwargs["start_new_session"] = True - proc = subprocess.Popen(command, **popen_kwargs) + proc = subprocess.Popen(command, **popen_kwargs, stdin=subprocess.DEVNULL) try: stdout, stderr = proc.communicate(timeout=timeout) except subprocess.TimeoutExpired as exc: @@ -882,6 +883,7 @@ def _convert_to_opus(mp3_path: str) -> Optional[str]: ["ffmpeg", "-i", mp3_path, "-acodec", "libopus", "-ac", "1", "-b:a", "64k", "-vbr", "off", ogg_path, "-y"], capture_output=True, timeout=30, + stdin=subprocess.DEVNULL, ) if result.returncode != 0: logger.warning("ffmpeg conversion failed with return code %d: %s", @@ -1504,7 +1506,7 @@ def _generate_gemini_tts(text: str, output_path: str, tts_config: Dict[str, Any] ] else: cmd = [ffmpeg, "-i", wav_path, "-y", "-loglevel", "error", output_path] - result = subprocess.run(cmd, capture_output=True, timeout=30) + result = subprocess.run(cmd, capture_output=True, timeout=30, stdin=subprocess.DEVNULL) if result.returncode != 0: stderr = result.stderr.decode("utf-8", errors="ignore")[:300] raise RuntimeError(f"ffmpeg conversion failed: {stderr}") @@ -1587,7 +1589,7 @@ def _generate_neutts(text: str, output_path: str, tts_config: Dict[str, Any]) -> "--device", device, ] - result = subprocess.run(cmd, capture_output=True, text=True, timeout=120) + result = subprocess.run(cmd, capture_output=True, text=True, timeout=120, stdin=subprocess.DEVNULL) if result.returncode != 0: stderr = result.stderr.strip() # Filter out the "OK:" line from stderr @@ -1599,7 +1601,7 @@ def _generate_neutts(text: str, output_path: str, tts_config: Dict[str, Any]) -> ffmpeg = shutil.which("ffmpeg") if ffmpeg: conv_cmd = [ffmpeg, "-i", wav_path, "-y", "-loglevel", "error", output_path] - subprocess.run(conv_cmd, check=True, timeout=30) + subprocess.run(conv_cmd, check=True, timeout=30, stdin=subprocess.DEVNULL) os.remove(wav_path) else: # No ffmpeg — just rename the WAV to the expected path @@ -1670,6 +1672,7 @@ def _resolve_piper_voice_path(voice: str, download_dir: Path) -> str: [_sys.executable, "-m", "piper.download_voices", voice, "--download-dir", str(download_dir)], capture_output=True, text=True, timeout=300, + stdin=subprocess.DEVNULL, ) except subprocess.TimeoutExpired as exc: raise RuntimeError( @@ -1757,7 +1760,7 @@ def _generate_piper_tts(text: str, output_path: str, tts_config: Dict[str, Any]) ffmpeg = shutil.which("ffmpeg") if ffmpeg: conv_cmd = [ffmpeg, "-i", wav_path, "-y", "-loglevel", "error", output_path] - subprocess.run(conv_cmd, check=True, timeout=30) + subprocess.run(conv_cmd, check=True, timeout=30, stdin=subprocess.DEVNULL) try: os.remove(wav_path) except OSError: @@ -1823,7 +1826,7 @@ def _generate_kittentts(text: str, output_path: str, tts_config: Dict[str, Any]) ffmpeg = shutil.which("ffmpeg") if ffmpeg: conv_cmd = [ffmpeg, "-i", wav_path, "-y", "-loglevel", "error", output_path] - subprocess.run(conv_cmd, check=True, timeout=30) + subprocess.run(conv_cmd, check=True, timeout=30, stdin=subprocess.DEVNULL) os.remove(wav_path) else: # No ffmpeg — rename the WAV to the expected path diff --git a/tools/voice_mode.py b/tools/voice_mode.py index 5d75f3c2068..d000e29d59d 100644 --- a/tools/voice_mode.py +++ b/tools/voice_mode.py @@ -75,6 +75,7 @@ def _termux_api_app_installed() -> bool: text=True, timeout=5, check=False, + stdin=subprocess.DEVNULL, ) return "package:com.termux.api" in (result.stdout or "") except Exception: @@ -388,7 +389,7 @@ class TermuxAudioRecorder: "-c", str(CHANNELS), ] try: - subprocess.run(command, capture_output=True, text=True, timeout=15, check=True) + subprocess.run(command, capture_output=True, text=True, timeout=15, check=True, stdin=subprocess.DEVNULL) except subprocess.CalledProcessError as e: details = (e.stderr or e.stdout or str(e)).strip() raise RuntimeError(f"Termux microphone start failed: {details}") from e @@ -405,7 +406,7 @@ class TermuxAudioRecorder: mic_cmd = _termux_microphone_command() if not mic_cmd: return - subprocess.run([mic_cmd, "-q"], capture_output=True, text=True, timeout=15, check=False) + subprocess.run([mic_cmd, "-q"], capture_output=True, text=True, timeout=15, check=False, stdin=subprocess.DEVNULL) def stop(self) -> Optional[str]: with self._lock: @@ -1095,7 +1096,7 @@ def play_audio_file(file_path: str) -> bool: exe = shutil.which(cmd[0]) if exe: try: - proc = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + proc = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL) with _playback_lock: _active_playback = proc proc.wait(timeout=300) diff --git a/tui_gateway/server.py b/tui_gateway/server.py index 0e309bb4c86..75e629c9b56 100644 --- a/tui_gateway/server.py +++ b/tui_gateway/server.py @@ -1041,6 +1041,7 @@ def _git_branch_for_cwd(cwd: str) -> str: text=True, timeout=1.5, check=False, + stdin=subprocess.DEVNULL, ) if result.returncode == 0: branch = result.stdout.strip() @@ -1052,6 +1053,7 @@ def _git_branch_for_cwd(cwd: str) -> str: text=True, timeout=1.5, check=False, + stdin=subprocess.DEVNULL, ) return head.stdout.strip() if head.returncode == 0 else "" except Exception: @@ -5571,7 +5573,7 @@ def _(rid, params: dict) -> dict: str(pdf_path), str(out_prefix), ] try: - res = subprocess.run(argv, capture_output=True, text=True, timeout=120) + res = subprocess.run(argv, capture_output=True, text=True, timeout=120, stdin=subprocess.DEVNULL) except subprocess.TimeoutExpired: return _err(rid, 5028, "pdftoppm timed out (>120s)") if res.returncode != 0: @@ -6845,6 +6847,7 @@ def _(rid, params: dict) -> dict: timeout=min(int(params.get("timeout", 240)), 600), cwd=os.getcwd(), env=os.environ.copy(), + stdin=subprocess.DEVNULL, ) parts = [r.stdout or "", r.stderr or ""] out = "\n".join(p for p in parts if p).strip() or "(no output)" @@ -6905,6 +6908,7 @@ def _(rid, params: dict) -> dict: capture_output=True, text=True, timeout=30, + stdin=subprocess.DEVNULL, ) output = ( (r.stdout or "") @@ -7295,6 +7299,7 @@ def _list_repo_files(root: str) -> list[str]: capture_output=True, timeout=2.0, check=False, + stdin=subprocess.DEVNULL, ) if top_result.returncode == 0: top = top_result.stdout.decode("utf-8", "replace").strip() @@ -7312,6 +7317,7 @@ def _list_repo_files(root: str) -> list[str]: capture_output=True, timeout=2.0, check=False, + stdin=subprocess.DEVNULL, ) if list_result.returncode == 0: for p in list_result.stdout.decode("utf-8", "replace").split("\0"): @@ -9045,7 +9051,8 @@ def _(rid, params: dict) -> dict: return _err(rid, 5001, "shell.exec unavailable: approval safety module not importable") try: r = subprocess.run( - cmd, shell=True, capture_output=True, text=True, timeout=30, cwd=os.getcwd() + cmd, shell=True, capture_output=True, text=True, timeout=30, cwd=os.getcwd(), + stdin=subprocess.DEVNULL, ) return _ok( rid,