gramit 0.2.0__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.
- gramit-0.2.0/.github/workflows/release.yaml +56 -0
- gramit-0.2.0/.github/workflows/tests.yaml +32 -0
- gramit-0.2.0/.gitignore +176 -0
- gramit-0.2.0/.python-version +1 -0
- gramit-0.2.0/CHANGELOG.md +53 -0
- gramit-0.2.0/DESIGN.md +81 -0
- gramit-0.2.0/GEMINI.md +51 -0
- gramit-0.2.0/LICENSE +21 -0
- gramit-0.2.0/PKG-INFO +137 -0
- gramit-0.2.0/README.md +109 -0
- gramit-0.2.0/ROADMAP.md +102 -0
- gramit-0.2.0/examples/reverse_echo.py +22 -0
- gramit-0.2.0/journal/2026-02-22.md +13 -0
- gramit-0.2.0/journal/2026-02-23.md +238 -0
- gramit-0.2.0/makefile +77 -0
- gramit-0.2.0/pyproject.toml +56 -0
- gramit-0.2.0/src/gramit/__init__.py +1 -0
- gramit-0.2.0/src/gramit/cli.py +209 -0
- gramit-0.2.0/src/gramit/debouncer.py +72 -0
- gramit-0.2.0/src/gramit/filters.py +0 -0
- gramit-0.2.0/src/gramit/orchestrator.py +106 -0
- gramit-0.2.0/src/gramit/router.py +87 -0
- gramit-0.2.0/src/gramit/telegram.py +71 -0
- gramit-0.2.0/tests/test_debouncer.py +52 -0
- gramit-0.2.0/tests/test_orchestrator.py +57 -0
- gramit-0.2.0/tests/test_router.py +70 -0
- gramit-0.2.0/tests/test_telegram.py +94 -0
- gramit-0.2.0/uv.lock +771 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: Release Pipeline
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [created]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- name: Checkout repository
|
|
12
|
+
uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- name: Set up Python
|
|
15
|
+
uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.12"
|
|
18
|
+
|
|
19
|
+
- name: Install uv
|
|
20
|
+
run: curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
21
|
+
|
|
22
|
+
- name: Install dependencies
|
|
23
|
+
run: uv sync --all-extras --all-groups --no-editable
|
|
24
|
+
|
|
25
|
+
- name: Run format check
|
|
26
|
+
run: uv run ruff check
|
|
27
|
+
|
|
28
|
+
- name: Run tests with coverage
|
|
29
|
+
run: uv run pytest --cov=gramit --cov-report=xml --cov-report=term
|
|
30
|
+
|
|
31
|
+
- name: Upload coverage to Codecov
|
|
32
|
+
uses: codecov/codecov-action@v4
|
|
33
|
+
with:
|
|
34
|
+
files: ./coverage.xml
|
|
35
|
+
fail_ci_if_error: false
|
|
36
|
+
|
|
37
|
+
publish-pypi:
|
|
38
|
+
needs: test
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
steps:
|
|
41
|
+
- name: Checkout repository
|
|
42
|
+
uses: actions/checkout@v4
|
|
43
|
+
|
|
44
|
+
- name: Set up Python
|
|
45
|
+
uses: actions/setup-python@v5
|
|
46
|
+
with:
|
|
47
|
+
python-version: "3.12"
|
|
48
|
+
|
|
49
|
+
- name: Install uv
|
|
50
|
+
run: curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
51
|
+
|
|
52
|
+
- name: Build package
|
|
53
|
+
run: uv build
|
|
54
|
+
|
|
55
|
+
- name: Publish to PyPI
|
|
56
|
+
run: uv publish --token ${{ secrets.PYPI_TOKEN }}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: Run Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout repository
|
|
15
|
+
uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Set up Python
|
|
18
|
+
uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
|
|
22
|
+
- name: Install uv
|
|
23
|
+
run: curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: uv sync --all-extras --all-groups --no-editable
|
|
27
|
+
|
|
28
|
+
- name: Run format check
|
|
29
|
+
run: uv run ruff check
|
|
30
|
+
|
|
31
|
+
- name: Run tests with coverage
|
|
32
|
+
run: uv run pytest --cov=gramit --cov-report=xml --cov-report=term
|
gramit-0.2.0/.gitignore
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Created by https://www.toptal.com/developers/gitignore/api/python
|
|
2
|
+
# Edit at https://www.toptal.com/developers/gitignore?templates=python
|
|
3
|
+
|
|
4
|
+
### Python ###
|
|
5
|
+
# Byte-compiled / optimized / DLL files
|
|
6
|
+
__pycache__/
|
|
7
|
+
*.py[cod]
|
|
8
|
+
*$py.class
|
|
9
|
+
|
|
10
|
+
# C extensions
|
|
11
|
+
*.so
|
|
12
|
+
|
|
13
|
+
# Distribution / packaging
|
|
14
|
+
.Python
|
|
15
|
+
build/
|
|
16
|
+
develop-eggs/
|
|
17
|
+
dist/
|
|
18
|
+
downloads/
|
|
19
|
+
eggs/
|
|
20
|
+
.eggs/
|
|
21
|
+
lib/
|
|
22
|
+
lib64/
|
|
23
|
+
parts/
|
|
24
|
+
sdist/
|
|
25
|
+
var/
|
|
26
|
+
wheels/
|
|
27
|
+
share/python-wheels/
|
|
28
|
+
*.egg-info/
|
|
29
|
+
.installed.cfg
|
|
30
|
+
*.egg
|
|
31
|
+
MANIFEST
|
|
32
|
+
|
|
33
|
+
# PyInstaller
|
|
34
|
+
# Usually these files are written by a python script from a template
|
|
35
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
36
|
+
*.manifest
|
|
37
|
+
*.spec
|
|
38
|
+
|
|
39
|
+
# Installer logs
|
|
40
|
+
pip-log.txt
|
|
41
|
+
pip-delete-this-directory.txt
|
|
42
|
+
|
|
43
|
+
# Unit test / coverage reports
|
|
44
|
+
htmlcov/
|
|
45
|
+
.tox/
|
|
46
|
+
.nox/
|
|
47
|
+
.coverage
|
|
48
|
+
.coverage.*
|
|
49
|
+
.cache
|
|
50
|
+
nosetests.xml
|
|
51
|
+
coverage.xml
|
|
52
|
+
*.cover
|
|
53
|
+
*.py,cover
|
|
54
|
+
.hypothesis/
|
|
55
|
+
.pytest_cache/
|
|
56
|
+
cover/
|
|
57
|
+
|
|
58
|
+
# Translations
|
|
59
|
+
*.mo
|
|
60
|
+
*.pot
|
|
61
|
+
|
|
62
|
+
# Django stuff:
|
|
63
|
+
*.log
|
|
64
|
+
local_settings.py
|
|
65
|
+
db.sqlite3
|
|
66
|
+
db.sqlite3-journal
|
|
67
|
+
|
|
68
|
+
# Flask stuff:
|
|
69
|
+
instance/
|
|
70
|
+
.webassets-cache
|
|
71
|
+
|
|
72
|
+
# Scrapy stuff:
|
|
73
|
+
.scrapy
|
|
74
|
+
|
|
75
|
+
# Sphinx documentation
|
|
76
|
+
docs/_build/
|
|
77
|
+
|
|
78
|
+
# PyBuilder
|
|
79
|
+
.pybuilder/
|
|
80
|
+
target/
|
|
81
|
+
|
|
82
|
+
# Jupyter Notebook
|
|
83
|
+
.ipynb_checkpoints
|
|
84
|
+
|
|
85
|
+
# IPython
|
|
86
|
+
profile_default/
|
|
87
|
+
ipython_config.py
|
|
88
|
+
|
|
89
|
+
# pyenv
|
|
90
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
91
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
92
|
+
# .python-version
|
|
93
|
+
|
|
94
|
+
# pipenv
|
|
95
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
96
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
97
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
98
|
+
# install all needed dependencies.
|
|
99
|
+
#Pipfile.lock
|
|
100
|
+
|
|
101
|
+
# poetry
|
|
102
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
103
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
104
|
+
# commonly ignored for libraries.
|
|
105
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
106
|
+
#poetry.lock
|
|
107
|
+
|
|
108
|
+
# pdm
|
|
109
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
110
|
+
#pdm.lock
|
|
111
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
112
|
+
# in version control.
|
|
113
|
+
# https://pdm.fming.dev/#use-with-ide
|
|
114
|
+
.pdm.toml
|
|
115
|
+
|
|
116
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
117
|
+
__pypackages__/
|
|
118
|
+
|
|
119
|
+
# Celery stuff
|
|
120
|
+
celerybeat-schedule
|
|
121
|
+
celerybeat.pid
|
|
122
|
+
|
|
123
|
+
# SageMath parsed files
|
|
124
|
+
*.sage.py
|
|
125
|
+
|
|
126
|
+
# Environments
|
|
127
|
+
.env
|
|
128
|
+
.venv
|
|
129
|
+
env/
|
|
130
|
+
venv/
|
|
131
|
+
ENV/
|
|
132
|
+
env.bak/
|
|
133
|
+
venv.bak/
|
|
134
|
+
|
|
135
|
+
# Spyder project settings
|
|
136
|
+
.spyderproject
|
|
137
|
+
.spyproject
|
|
138
|
+
|
|
139
|
+
# Rope project settings
|
|
140
|
+
.ropeproject
|
|
141
|
+
|
|
142
|
+
# mkdocs documentation
|
|
143
|
+
/site
|
|
144
|
+
|
|
145
|
+
# mypy
|
|
146
|
+
.mypy_cache/
|
|
147
|
+
.dmypy.json
|
|
148
|
+
dmypy.json
|
|
149
|
+
|
|
150
|
+
# Pyre type checker
|
|
151
|
+
.pyre/
|
|
152
|
+
|
|
153
|
+
# pytype static type analyzer
|
|
154
|
+
.pytype/
|
|
155
|
+
|
|
156
|
+
# Cython debug symbols
|
|
157
|
+
cython_debug/
|
|
158
|
+
|
|
159
|
+
# PyCharm
|
|
160
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
161
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
162
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
163
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
164
|
+
#.idea/
|
|
165
|
+
|
|
166
|
+
### Python Patch ###
|
|
167
|
+
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
|
168
|
+
poetry.toml
|
|
169
|
+
|
|
170
|
+
# ruff
|
|
171
|
+
.ruff_cache/
|
|
172
|
+
|
|
173
|
+
# LSP config files
|
|
174
|
+
pyrightconfig.json
|
|
175
|
+
|
|
176
|
+
# End of https://www.toptal.com/developers/gitignore/api/python
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [v0.1.2]
|
|
9
|
+
|
|
10
|
+
### Security
|
|
11
|
+
- Implemented rate-limiting and output trimming in `OutputRouter` and `AsyncDebouncer` to prevent memory exhaustion and respect Telegram's message size limits.
|
|
12
|
+
- Restricted local `.env` file permissions to owner-only (`600`) to protect sensitive tokens.
|
|
13
|
+
- Added a prominent security warning to `README.md` regarding the risks of remote command execution.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- Added detailed installation instructions for `pip`, `pipx`, and `uv` to `README.md`.
|
|
17
|
+
- Added project descriptors (keywords, classifiers, and URLs) to `pyproject.toml` for better metadata and discoverability.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
- Corrected a missing f-string in `cli.py` logging.
|
|
21
|
+
|
|
22
|
+
## [v0.0.1]
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- Ensured `Ctrl+C` triggers graceful shutdown by restructuring `try...except asyncio.CancelledError` block within `async with application:` in `cli.py`.
|
|
26
|
+
- Resolved `UnboundLocalError` for `shutdown_event` by moving its initialization to an earlier point in `cli.py`.
|
|
27
|
+
- Implemented graceful shutdown for `Ctrl+C` and `/quit` command by using `asyncio.Event` to signal shutdown and ensuring all components are properly terminated.
|
|
28
|
+
- Replaced `application.run_until_disconnected()` with `async with application:` and `await asyncio.Future()` for correct asynchronous Telegram bot lifecycle management in `cli.py`.
|
|
29
|
+
- Reverted temporary debugging changes in `cli.py` and `telegram.py` to restore intended application flow.
|
|
30
|
+
- Corrected `SyntaxError` in `src/gramit/telegram.py` related to an unmatched parenthesis.
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
- Initial and goodbye messages sent to Telegram upon gramit startup/shutdown.
|
|
34
|
+
- `/quit` Telegram command to gracefully terminate the orchestrated process.
|
|
35
|
+
- `examples/reverse_echo.py` script for interactive testing.
|
|
36
|
+
- Console script `gramit` for easier execution.
|
|
37
|
+
- `--register` mode to easily find a user's chat ID.
|
|
38
|
+
- Support for `GRAMIT_CHAT_ID` environment variable as an alternative to `--chat-id`.
|
|
39
|
+
- Main application entrypoint (`cli.py`) to integrate all components.
|
|
40
|
+
- `InputRouter` to handle and authorize incoming Telegram messages.
|
|
41
|
+
- `OutputRouter` to process and send output in line-buffered mode.
|
|
42
|
+
- `AsyncDebouncer` utility to batch items after a period of inactivity.
|
|
43
|
+
- I/O handling (`read`/`write`) to the `Orchestrator`.
|
|
44
|
+
- `Orchestrator` class to manage child processes in a pseudo-terminal (PTY).
|
|
45
|
+
- Initial project structure with `uv`, `pytest`, and core directories.
|
|
46
|
+
- `GEMINI.md` with project development guidelines.
|
|
47
|
+
- `DESIGN.md` v2.1, a robust architectural plan for the application.
|
|
48
|
+
- `ROADMAP.md` detailing the phased implementation plan.
|
|
49
|
+
- `CHANGELOG.md` to track notable changes.
|
|
50
|
+
- `journal/` directory for detailed daily progress logs.
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
- Overhauled the initial system design to address critical security, dependency, and usability issues.
|
gramit-0.2.0/DESIGN.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Gramit: Architectural & System Design Document (v2.1)
|
|
2
|
+
|
|
3
|
+
## 1. System Overview & Design Principles
|
|
4
|
+
|
|
5
|
+
Gramit is a secure, self-contained Python orchestrator that bridges a local, command-line application with a remote Telegram interface. It allows a single, authorized user to control and monitor any CLI process from their Telegram chat.
|
|
6
|
+
|
|
7
|
+
This design adheres to the following principles:
|
|
8
|
+
|
|
9
|
+
* **Security First:** The system is locked down by default. It requires explicit user authorization and uses secure methods for handling credentials.
|
|
10
|
+
* **Zero External Dependencies:** The application is a pure Python tool and does not require external binaries like `tmux`. This enhances portability and reduces complexity.
|
|
11
|
+
* **Remote-First Interaction:** The primary interface is Telegram. Simultaneous local terminal interaction is not supported, which eliminates input race conditions and simplifies the design.
|
|
12
|
+
* **Intelligent TUI Parsing:** It is designed to extract meaningful text from Terminal User Interfaces (TUIs), filtering out UI "chrome" like borders and control panels.
|
|
13
|
+
|
|
14
|
+
## 2. Core Architecture
|
|
15
|
+
|
|
16
|
+
Gramit is built on `asyncio`. When launched, it starts a main orchestrator that runs the target command in a pseudo-terminal (PTY) and manages three core asynchronous components.
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+
|
|
20
|
+
### 2.1. The PTY Orchestrator
|
|
21
|
+
This component replaces the `tmux` dependency.
|
|
22
|
+
|
|
23
|
+
* **Initialization:** On startup, the orchestrator uses `os.forkpty()` to create a new child process running inside a pseudo-terminal. This correctly allocates a PTY, ensuring that interactive applications (including TUIs) behave as expected.
|
|
24
|
+
* **Lifecycle Management:** The orchestrator owns the child process and the master file descriptor for the PTY. It is responsible for gracefully shutting down all components when the child process exits.
|
|
25
|
+
|
|
26
|
+
### 2.2. The Input Router (Telegram → PTY)
|
|
27
|
+
This component handles incoming commands from Telegram.
|
|
28
|
+
|
|
29
|
+
* **Authorization:** It uses a Telegram bot library (e.g., `python-telegram-bot`) to listen for messages. It will **only** process messages from a single, pre-authorized `chat_id` provided at startup. All other messages are discarded.
|
|
30
|
+
* **Injection:** When an authorized message is received, its text content is written directly to the master PTY file descriptor, which passes it to the `stdin` of the running child process.
|
|
31
|
+
|
|
32
|
+
### 2.3. The Output Router (PTY → Telegram)
|
|
33
|
+
This component intelligently captures, processes, and transmits output from the application back to the user. It operates in one of two modes.
|
|
34
|
+
|
|
35
|
+
#### 2.3.1. TUI Mode (Default)
|
|
36
|
+
This mode is designed for interactive applications like `htop`, `vim`, or other curses-based programs.
|
|
37
|
+
|
|
38
|
+
1. **Virtual Screen:** The raw byte stream from the master PTY (which includes ANSI escape codes) is fed into an in-memory terminal emulator powered by the `pyte` library.
|
|
39
|
+
2. **Stateful Rendering:** The `pyte` screen object processes the escape codes and maintains a complete, 2D grid representing the terminal's current visual state.
|
|
40
|
+
3. **Snapshotting:** A debounced `asyncio` task runs at a configurable interval (e.g., every 2 seconds). In each interval, it dumps the current text content of the `pyte` screen.
|
|
41
|
+
4. **Heuristic Filtering (Content Extraction):** Before transmission, the raw text snapshot is passed through a filtering pipeline to remove common TUI "chrome." This is a best-effort attempt to extract semantic content and includes:
|
|
42
|
+
* Removing lines that primarily consist of box-drawing characters (e.g., `│`, `─`, `┌`).
|
|
43
|
+
* Stripping common control and menu indicators (e.g., lines containing patterns like "F1 Help", "F10 Quit").
|
|
44
|
+
* Trimming leading/trailing whitespace from each line and removing empty lines from the top and bottom of the snapshot.
|
|
45
|
+
5. **Transmission:** The **cleaned** text snapshot is wrapped in a Telegram markdown code block and sent using `editMessageText` to update a single, persistent "screen" message. If the snapshot exceeds Telegram's 4096-character limit, it is truncated with a warning.
|
|
46
|
+
|
|
47
|
+
#### 2.3.2. Line Mode (`--line-mode`)
|
|
48
|
+
This mode is for simple, non-interactive applications that produce a stream of log-like output.
|
|
49
|
+
|
|
50
|
+
1. **Line Buffering:** The raw output from the PTY is decoded and buffered line by line.
|
|
51
|
+
2. **Debounced Flush:** The debounced task flushes this buffer of lines.
|
|
52
|
+
3. **Transmission:** The collected lines are joined and sent as one or more *new messages* using `sendMessage`. The router automatically handles splitting content across multiple messages if it exceeds the character limit.
|
|
53
|
+
|
|
54
|
+
## 3. Security Model
|
|
55
|
+
|
|
56
|
+
The security of the system is paramount and is addressed through two key mechanisms.
|
|
57
|
+
|
|
58
|
+
### 3.1. Credential Management
|
|
59
|
+
The Telegram Bot Token is treated as a sensitive secret.
|
|
60
|
+
|
|
61
|
+
* **Environment Variable:** The token **must** be provided via an environment variable (`GRAMIT_TELEGRAM_TOKEN`).
|
|
62
|
+
* **`.env` Support:** For ease of local development, the application will automatically load this variable from a `.env` file in the project root if it exists (using `python-dotenv`).
|
|
63
|
+
* The insecure `-t` command-line argument has been removed.
|
|
64
|
+
|
|
65
|
+
### 3.2. Access Control
|
|
66
|
+
Access to the bot is strictly controlled.
|
|
67
|
+
|
|
68
|
+
* **Required `chat_id`:** The application requires a `--chat-id <ID>` argument on startup. This locks the bot to a specific user or group.
|
|
69
|
+
* **Helper Utility:** A small utility script will be provided (`gramit-get-chat-id`) to help users easily find their `chat_id`.
|
|
70
|
+
|
|
71
|
+
## 4. Summary of Countermeasures
|
|
72
|
+
|
|
73
|
+
This revised design directly addresses the challenges of the initial proposal:
|
|
74
|
+
|
|
75
|
+
| Issue | Countermeasure |
|
|
76
|
+
| :--- | :--- |
|
|
77
|
+
| **TUI Incompatibility** | Replaced `tail -f` approach with a `pyte`-based virtual screen and added a heuristic filtering layer to extract meaningful content. |
|
|
78
|
+
| **`tmux` Dependency** | Eliminated `tmux` entirely by using Python's native `os.forkpty()` to manage the pseudo-terminal directly. |
|
|
79
|
+
| **Input Race Conditions** | Removed the "local attach" feature. By making Telegram the sole source of input, race conditions are impossible. |
|
|
80
|
+
| **Output Limits** | Implemented two distinct output modes (TUI and Line) with intelligent handling of message edits, new messages, and character limits. |
|
|
81
|
+
| **Security Flaws** | Enforced secure token handling via environment variables and mandated `chat_id` authorization to lock down the bot. |
|
gramit-0.2.0/GEMINI.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Gemini Project Guidelines
|
|
2
|
+
|
|
3
|
+
This document outlines the conventions and procedures to be followed by the Gemini agent for this project.
|
|
4
|
+
|
|
5
|
+
## Core Workflow
|
|
6
|
+
|
|
7
|
+
A strict, iterative, and well-documented workflow must be followed for all projects. This ensures the project history is clear and documentation stays current with the code.
|
|
8
|
+
|
|
9
|
+
The following files are central to this workflow and must be kept in sync with all development activities:
|
|
10
|
+
|
|
11
|
+
- **`ROADMAP.md`**: Outlines the high-level development plan. It should be updated as major phases or tasks are completed.
|
|
12
|
+
- **`CHANGELOG.md`**: Contains a user-facing summary of notable changes. An entry should be added for every significant feature, fix, or improvement.
|
|
13
|
+
- **`journal/`**: A directory containing detailed, chronological logs of all actions, thoughts, and decisions made during development, with one file per day (e.g., `journal/YYYY-MM-DD.md`).
|
|
14
|
+
|
|
15
|
+
The development process is as follows:
|
|
16
|
+
|
|
17
|
+
1. **Iterate Step-by-Step**: Work in small, incremental steps, following the project's specific development methodology.
|
|
18
|
+
2. **Document as You Go**: After each step, immediately update all relevant documentation (`ROADMAP.md`, `CHANGELOG.md`, `journal/`).
|
|
19
|
+
3. **Commit Often**: After each meaningful and atomic change (including its documentation), create a commit with a clear message.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Project-Specific Guidelines
|
|
24
|
+
|
|
25
|
+
This section details the specific tooling and methodologies for the **gramit** project.
|
|
26
|
+
|
|
27
|
+
### Project Overview
|
|
28
|
+
This is a Python 3.12 project.
|
|
29
|
+
|
|
30
|
+
### Environment and Dependency Management
|
|
31
|
+
- **Tooling:** We will use `uv` for all environment and package management tasks.
|
|
32
|
+
- **Running commands:** Any scripts or commands should be executed within the `uv` environment using `uv run`. For example: `uv run python my_script.py` or `uv run pytest`.
|
|
33
|
+
- **Adding dependencies:** Dependencies must be added using `uv add <package_name>`. For dev-only dependencies, use `uv add --dev <package_name>`.
|
|
34
|
+
- **Dependency file:** Do not manually edit the `dependencies` or `dev-dependencies` sections in `pyproject.toml`.
|
|
35
|
+
|
|
36
|
+
### Development Methodology
|
|
37
|
+
- **Test-Driven Development (TDD):** We will strictly follow TDD.
|
|
38
|
+
1. Write a failing test that clearly defines the desired functionality.
|
|
39
|
+
2. Run the test to confirm that it fails as expected.
|
|
40
|
+
3. Write the minimum amount of code necessary to make the test pass.
|
|
41
|
+
4. Run all tests to confirm they all pass.
|
|
42
|
+
5. Refactor the code as needed, ensuring tests continue to pass.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## General Coding Standards
|
|
47
|
+
|
|
48
|
+
### Documentation and Commenting
|
|
49
|
+
- **Public Functions:** All public functions must have clear and comprehensive docstrings explaining their purpose, arguments, and return values.
|
|
50
|
+
- **Inline Comments:** Avoid unnecessary inline comments. Code should be as self-documenting as possible.
|
|
51
|
+
- **Comment Style:** When comments are necessary, they should explain the *why* (the logic or reasoning) behind a piece of code, not the *what* (the implementation details).
|
gramit-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alejandro Piad
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
gramit-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gramit
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A tool to bridge local CLI applications with a remote Telegram interface.
|
|
5
|
+
Project-URL: Homepage, https://github.com/apiad/gramit
|
|
6
|
+
Project-URL: Repository, https://github.com/apiad/gramit
|
|
7
|
+
Project-URL: Issues, https://github.com/apiad/gramit/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/apiad/gramit/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: Alejandro Piad <alepiad@gmail.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: bridge,cli,remote-control,telegram,terminal
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: System Administrators
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Communications :: Chat
|
|
21
|
+
Classifier: Topic :: System :: Systems Administration
|
|
22
|
+
Classifier: Topic :: Utilities
|
|
23
|
+
Requires-Python: >=3.12
|
|
24
|
+
Requires-Dist: pyte>=0.8.2
|
|
25
|
+
Requires-Dist: python-dotenv>=1.2.1
|
|
26
|
+
Requires-Dist: python-telegram-bot>=22.6
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# Gramit
|
|
30
|
+
|
|
31
|
+
[](https://github.com/apiad/gramit/actions/workflows/tests.yaml)
|
|
32
|
+
[](https://opensource.org/licenses/MIT)
|
|
33
|
+
[](https://www.python.org/downloads/release/python-3120/)
|
|
34
|
+
|
|
35
|
+
> [!CAUTION]
|
|
36
|
+
> **SECURITY WARNING: REMOTE ACCESS RISK**
|
|
37
|
+
>
|
|
38
|
+
> Gramit provides a bridge between your local machine and a remote Telegram interface. **This allows remote command execution.**
|
|
39
|
+
>
|
|
40
|
+
> If you run a shell (like `/bin/bash` or `cmd.exe`) or any interactive tool through Gramit, anyone with access to your Telegram bot (and whose Chat ID is authorized) has **full control over your machine**.
|
|
41
|
+
>
|
|
42
|
+
> **USE ONLY IF YOU ARE HIGHLY SECURITY-AWARE.**
|
|
43
|
+
> - Never share your `GRAMIT_TELEGRAM_TOKEN`.
|
|
44
|
+
> - Ensure your `GRAMIT_CHAT_ID` is correctly configured to *your* ID only.
|
|
45
|
+
> - Be extremely cautious when bridging shells or administrative tools.
|
|
46
|
+
|
|
47
|
+
Gramit bridges a local CLI application with a remote Telegram interface. It allows you to run any long-running command on your machine and interact with it from anywhere using Telegram. While designed to be generic for any CLI, it's particularly useful for interactive AI CLIs like **Gemini CLI**, **Claude Code**, or similar tools where you want to maintain a persistent session and interact remotely.
|
|
48
|
+
|
|
49
|
+
## How it Works
|
|
50
|
+
|
|
51
|
+
Gramit acts as a conduit between your local command-line application and your Telegram bot.
|
|
52
|
+
- **Input Redirection:** Any message you send to your Telegram bot is piped directly to the `stdin` of the running local command.
|
|
53
|
+
- **Output Capture:** All `stdout` from your local command is captured and sent back to you as a Telegram message.
|
|
54
|
+
- **Session Management:** It maintains a persistent session, allowing for continuous interaction with your CLI application.
|
|
55
|
+
|
|
56
|
+
## Setup
|
|
57
|
+
|
|
58
|
+
1. **Installation**
|
|
59
|
+
|
|
60
|
+
You can install Gramit using your favorite Python package manager:
|
|
61
|
+
|
|
62
|
+
**Using `pip`:**
|
|
63
|
+
```sh
|
|
64
|
+
pip install gramit
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Using `pipx` (Recommended for CLI tools):**
|
|
68
|
+
```sh
|
|
69
|
+
pipx install gramit
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Using `uv`:**
|
|
73
|
+
```sh
|
|
74
|
+
# Run without installing
|
|
75
|
+
uvx gramit --help
|
|
76
|
+
|
|
77
|
+
# Or install as a tool
|
|
78
|
+
uv tool install gramit
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
2. **Get a Telegram Bot Token**
|
|
82
|
+
- Talk to the [@BotFather](https://t.me/BotFather) on Telegram.
|
|
83
|
+
- Create a new bot and copy the token it gives you.
|
|
84
|
+
|
|
85
|
+
3. **Set Environment Variables**
|
|
86
|
+
- Create a file named `.env` in the project root.
|
|
87
|
+
- Add the following line to it, replacing `YOUR_TOKEN_HERE` with the token you just got:
|
|
88
|
+
```
|
|
89
|
+
GRAMIT_TELEGRAM_TOKEN="YOUR_TOKEN_HERE"
|
|
90
|
+
```
|
|
91
|
+
- You can also optionally add your Chat ID to this file (see step 4):
|
|
92
|
+
```
|
|
93
|
+
GRAMIT_CHAT_ID="YOUR_CHAT_ID_HERE"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
4. **Find Your Chat ID**
|
|
97
|
+
- The easiest way to find your chat ID is to use Gramit's built-in registration mode. Run the following command:
|
|
98
|
+
```sh
|
|
99
|
+
gramit --register
|
|
100
|
+
```
|
|
101
|
+
- Now, send any message to your bot on Telegram. Gramit will print your Chat ID to the console and also reply with it.
|
|
102
|
+
- Once you have your ID, you can stop the command (`Ctrl_C`).
|
|
103
|
+
|
|
104
|
+
## Usage
|
|
105
|
+
|
|
106
|
+
Run `gramit` with your command. If you have not set `GRAMIT_CHAT_ID` in your `.env` file, you must also provide the `--chat-id` argument.
|
|
107
|
+
|
|
108
|
+
**Basic Example:**
|
|
109
|
+
```sh
|
|
110
|
+
# If GRAMIT_CHAT_ID is set in .env
|
|
111
|
+
gramit ping 8.8.8.8
|
|
112
|
+
|
|
113
|
+
# If GRAMIT_CHAT_ID is NOT set
|
|
114
|
+
gramit --chat-id YOUR_CHAT_ID ping 8.8.8.8
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
- Any text you send to your bot on Telegram will be piped to the `stdin` of the running command.
|
|
118
|
+
- Any `stdout` from the command will be sent back to you as a Telegram message.
|
|
119
|
+
|
|
120
|
+
**Interactive Example (Reverse Echo):**
|
|
121
|
+
```sh
|
|
122
|
+
gramit python examples/reverse_echo.py
|
|
123
|
+
```
|
|
124
|
+
Then, send messages to your bot on Telegram. It will echo them back in reverse.
|
|
125
|
+
|
|
126
|
+
**Features:**
|
|
127
|
+
- Upon starting, Gramit sends an initial message to Telegram indicating the command being run.
|
|
128
|
+
- When the orchestrated process ends, a "goodbye" message is sent to Telegram.
|
|
129
|
+
- Send `/quit` to your bot to gracefully terminate the running command from Telegram.
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
134
|
+
|
|
135
|
+
## Contributions
|
|
136
|
+
|
|
137
|
+
Contributions are welcome! If you have suggestions for improvements, new features, or bug fixes, please open an issue or submit a pull request.
|