<!-- TODO: save the last time terminal has content update, and use the timestamp for activity monitoring, signal 'terminal idle' event if no activity in the next X seconds -->
<div>
<p align="center"><h1 align="center">Terminal Executor</h1></p>
<p align="center">Terminal executor for AI agents, with ANSI escape sequence support and image screenshots.</p>
<p align="center">
<a href="https://github.com/james4ever0/termexec/blob/main/LICENSE"><img alt="License: UNLICENSE"
src="https://img.shields.io/badge/license-UNLICENSE-green.svg?style=flat"></a>
<a href="https://pypi.org/project/termexec/"><img alt="PyPI" src="https://img.shields.io/pypi/v/termexec"></a>
<a href="https://pepy.tech/projects/termexec"><img src="https://static.pepy.tech/badge/termexec" alt="PyPI Downloads"></a>
<a href="https://github.com/james4ever0/termexec"><img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
</p>
</div>
## Demo
Using termexec for solving the "vimgolf-test" challenge:

More info at [vimgolf-gym](https://github.com/james4ever0/vimgolf-gym)
## Installation
```bash
# install from pypi
pip install termexec
# or install the latest version from github
pip install git+https://github.com/james4ever0/termexec.git
```
Note: if your platform does not have prebuilt binaries of agg-python-bindings, just install cargo and rustc so the source code could compile.
## Usage
```python
from termexec import TerminalExecutor
import time
# Initializes executor with a command to run in terminal emulator, using avt as backend, with automatic context cleanup
with TerminalExecutor(['bash'], width=80, height=24) as executor:
# Waits for the terminal emulator to be ready.
time.sleep(1) # Adjust sleep time as necessary for your environment
# Or if you prefer to wait for terminal idle event
try:
executor.terminal.wait_for_terminal_idle(duration=1, timeout=10)
except TimeoutError:
print("Terminal did not become idle in time.")
# Get the current display of the terminal emulator as a string.
terminal_text = executor.display
print("Terminal Display:")
print(terminal_text)
# Send input to the terminal emulator.
executor.input("echo Hello, World!\n")
print("After input:")
print(executor.display)
# Saves the current display of the terminal emulator as a .png file
executor.screenshot("screenshot.png")
print("Screenshot saved as screenshot.png")
# Get the PID of the terminal process.
print("Terminal process PID:", executor.terminal.pty_process.pid)
```
## Alternatives
- Xterm.js running in phantomjs, electron or headless playwright
- LXterminal running in kiosk mode with x11vnc and novnc
## License
The Unlicense
Raw data
{
"_id": null,
"home_page": "https://github.com/james4ever0/termexec",
"name": "termexec",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": null,
"author": "James Brown",
"author_email": "randomvoidmail@foxmail.com",
"download_url": "https://files.pythonhosted.org/packages/2b/1a/2234b23ca0079a17bf8d54bb1cbaf697e2816cbce99f54628c8bf8d8045d/termexec-0.0.3.tar.gz",
"platform": null,
"description": "<!-- TODO: save the last time terminal has content update, and use the timestamp for activity monitoring, signal 'terminal idle' event if no activity in the next X seconds -->\n\n\n<div>\n<p align=\"center\"><h1 align=\"center\">Terminal Executor</h1></p>\n<p align=\"center\">Terminal executor for AI agents, with ANSI escape sequence support and image screenshots.</p>\n<p align=\"center\">\n<a href=\"https://github.com/james4ever0/termexec/blob/main/LICENSE\"><img alt=\"License: UNLICENSE\"\n src=\"https://img.shields.io/badge/license-UNLICENSE-green.svg?style=flat\"></a>\n<a href=\"https://pypi.org/project/termexec/\"><img alt=\"PyPI\" src=\"https://img.shields.io/pypi/v/termexec\"></a>\n<a href=\"https://pepy.tech/projects/termexec\"><img src=\"https://static.pepy.tech/badge/termexec\" alt=\"PyPI Downloads\"></a>\n<a href=\"https://github.com/james4ever0/termexec\"><img alt=\"Code style: black\" src=\"https://img.shields.io/badge/code%20style-black-000000.svg\"></a>\n</p>\n</div>\n\n## Demo\n\nUsing termexec for solving the \"vimgolf-test\" challenge:\n\n\n\nMore info at [vimgolf-gym](https://github.com/james4ever0/vimgolf-gym)\n\n## Installation\n\n```bash\n# install from pypi\npip install termexec\n\n# or install the latest version from github\npip install git+https://github.com/james4ever0/termexec.git\n```\n\nNote: if your platform does not have prebuilt binaries of agg-python-bindings, just install cargo and rustc so the source code could compile.\n\n## Usage\n\n```python\nfrom termexec import TerminalExecutor\nimport time\n\n# Initializes executor with a command to run in terminal emulator, using avt as backend, with automatic context cleanup\nwith TerminalExecutor(['bash'], width=80, height=24) as executor:\n # Waits for the terminal emulator to be ready.\n time.sleep(1) # Adjust sleep time as necessary for your environment\n\n # Or if you prefer to wait for terminal idle event\n try:\n executor.terminal.wait_for_terminal_idle(duration=1, timeout=10)\n except TimeoutError:\n print(\"Terminal did not become idle in time.\")\n\n # Get the current display of the terminal emulator as a string.\n terminal_text = executor.display\n print(\"Terminal Display:\")\n print(terminal_text)\n\n # Send input to the terminal emulator.\n executor.input(\"echo Hello, World!\\n\")\n print(\"After input:\")\n print(executor.display)\n\n # Saves the current display of the terminal emulator as a .png file\n executor.screenshot(\"screenshot.png\")\n print(\"Screenshot saved as screenshot.png\")\n\n # Get the PID of the terminal process.\n print(\"Terminal process PID:\", executor.terminal.pty_process.pid)\n```\n\n## Alternatives\n\n- Xterm.js running in phantomjs, electron or headless playwright\n\n- LXterminal running in kiosk mode with x11vnc and novnc\n\n## License\n\nThe Unlicense\n",
"bugtrack_url": null,
"license": null,
"summary": "Terminal executor for AI agents, with ANSI escape sequence support and image screenshots",
"version": "0.0.3",
"project_urls": {
"Homepage": "https://github.com/james4ever0/termexec"
},
"split_keywords": [],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "6d755234a4964b37a08c7feb02c6ec63e71b288b83d4d8459d2fe252ec7f7f4d",
"md5": "6a46f6f6399c92d0e0a891422ebab3ee",
"sha256": "d54ed5ce42b2beafbf1b406c0c2b019713baf1fa87cdf25c8e756ccbdd2679d8"
},
"downloads": -1,
"filename": "termexec-0.0.3-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "6a46f6f6399c92d0e0a891422ebab3ee",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": ">=3.8",
"size": 7079,
"upload_time": "2025-08-31T11:43:28",
"upload_time_iso_8601": "2025-08-31T11:43:28.591086Z",
"url": "https://files.pythonhosted.org/packages/6d/75/5234a4964b37a08c7feb02c6ec63e71b288b83d4d8459d2fe252ec7f7f4d/termexec-0.0.3-py2.py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "2b1a2234b23ca0079a17bf8d54bb1cbaf697e2816cbce99f54628c8bf8d8045d",
"md5": "9732ad7004eeca6c0dd3772e755369f9",
"sha256": "55d24eaa0278e8c9fa0b000174e54eb12ea3369d5d0520999de6d85e37ad56d1"
},
"downloads": -1,
"filename": "termexec-0.0.3.tar.gz",
"has_sig": false,
"md5_digest": "9732ad7004eeca6c0dd3772e755369f9",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 7352,
"upload_time": "2025-08-31T11:43:29",
"upload_time_iso_8601": "2025-08-31T11:43:29.989893Z",
"url": "https://files.pythonhosted.org/packages/2b/1a/2234b23ca0079a17bf8d54bb1cbaf697e2816cbce99f54628c8bf8d8045d/termexec-0.0.3.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-31 11:43:29",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "james4ever0",
"github_project": "termexec",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"requirements": [
{
"name": "agg-python-bindings",
"specs": [
[
"==",
"0.1.4"
]
]
},
{
"name": "ptyprocess",
"specs": [
[
"==",
"0.7.0"
]
]
},
{
"name": "pydantic",
"specs": []
}
],
"lcname": "termexec"
}