ai-ebash 0.1.2__py3-none-any.whl
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.
- ai_ebash-0.1.2.dist-info/METADATA +55 -0
- ai_ebash-0.1.2.dist-info/RECORD +11 -0
- ai_ebash-0.1.2.dist-info/WHEEL +5 -0
- ai_ebash-0.1.2.dist-info/entry_points.txt +2 -0
- ai_ebash-0.1.2.dist-info/top_level.txt +1 -0
- aiebash/__init__.py +0 -0
- aiebash/__main__.py +100 -0
- aiebash/api_client.py +49 -0
- aiebash/block_runner.py +30 -0
- aiebash/formatter_text.py +23 -0
- aiebash/settings.py +65 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: ai-ebash
|
3
|
+
Version: 0.1.2
|
4
|
+
Summary: Console utility for integrating artificial intelligence into a Linux terminal.
|
5
|
+
Author: Andrey Bochkarev
|
6
|
+
Author-email: andrey.bch.1976@gmail.com
|
7
|
+
License: MIT
|
8
|
+
Requires-Python: >=3.9
|
9
|
+
Description-Content-Type: text/markdown
|
10
|
+
Requires-Dist: certifi==2025.8.3
|
11
|
+
Requires-Dist: charset-normalizer==3.4.3
|
12
|
+
Requires-Dist: idna==3.10
|
13
|
+
Requires-Dist: markdown-it-py==3.0.0
|
14
|
+
Requires-Dist: mdurl==0.1.2
|
15
|
+
Requires-Dist: platformdirs==4.4.0
|
16
|
+
Requires-Dist: Pygments==2.19.2
|
17
|
+
Requires-Dist: requests==2.32.5
|
18
|
+
Requires-Dist: rich==14.1.0
|
19
|
+
Requires-Dist: urllib3==2.5.0
|
20
|
+
|
21
|
+
# Ai-bash!
|
22
|
+
Console utility for integrating artificial intelligence into a Linux terminal. Allows you to ask an AI question and execute the scripts and commands suggested by the AI in the terminal. It will be useful for novice Linux administrators.
|
23
|
+
|
24
|
+
The project is in the pre-alpha stage. In case of problems with the installation or operation of the Ai-bash utility, please contact me.
|
25
|
+
|
26
|
+
## Setup
|
27
|
+
|
28
|
+
### Ubuntu/Debian
|
29
|
+
Download `.deb` из [Releases](https://github.com/yourname/ai-bash/releases) and install:
|
30
|
+
|
31
|
+
```bash
|
32
|
+
sudo dpkg -i ai-bash_0.1.0-1_all.deb
|
33
|
+
sudo apt -f install # it will tighten up the missing dependencies
|
34
|
+
```
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
### Run
|
39
|
+
```bash
|
40
|
+
ai [-run] Your request to the AI
|
41
|
+
```
|
42
|
+
|
43
|
+
### Example
|
44
|
+
```bash
|
45
|
+
ai Write a script in bash that outputs a list of files in the current directory.
|
46
|
+
```
|
47
|
+
or
|
48
|
+
```bash
|
49
|
+
ai -run Write a script in bash that outputs a list of files in the current directory.
|
50
|
+
```
|
51
|
+
|
52
|
+
## Remove
|
53
|
+
```bash
|
54
|
+
sudo apt remove ai-bash
|
55
|
+
```
|
@@ -0,0 +1,11 @@
|
|
1
|
+
aiebash/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
aiebash/__main__.py,sha256=DHAyTjr2VmSnXP9VGLzUOVVJGTgErBUtDjWkizC2rWU,3720
|
3
|
+
aiebash/api_client.py,sha256=-hcgY3CP0lsV9Jct_-HQ6WpDosDChwalJjy4aFQ4CIM,2004
|
4
|
+
aiebash/block_runner.py,sha256=JNcdBklJmrlO7ZX7xXiO0fUGqrcNJiRqdwM-yr3OP3k,1599
|
5
|
+
aiebash/formatter_text.py,sha256=NRz_pCpakZ9k8477_4Gx1scDXrEbbOaOi-U7WRp6ZWo,847
|
6
|
+
aiebash/settings.py,sha256=Ml9_ixXl-DVpFYfCygMAhGSH-1luTxN22YYz1mHlsfw,2671
|
7
|
+
ai_ebash-0.1.2.dist-info/METADATA,sha256=ZpxRWic7mwoJDXe_2eelWAnENkSFDdBKKD5eHaG5iAw,1595
|
8
|
+
ai_ebash-0.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
9
|
+
ai_ebash-0.1.2.dist-info/entry_points.txt,sha256=bstvFHHX76KHqYpjM8FPoRVvNbQprST74CI30Mr5TIM,45
|
10
|
+
ai_ebash-0.1.2.dist-info/top_level.txt,sha256=JJ-bWdhWlFCfCQGLhmqzN4_bNO0pfVPRsdbx8o9A9AA,8
|
11
|
+
ai_ebash-0.1.2.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
aiebash
|
aiebash/__init__.py
ADDED
File without changes
|
aiebash/__main__.py
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
import sys
|
3
|
+
from pathlib import Path
|
4
|
+
|
5
|
+
# Убедимся, что parent (src) в sys.path, чтобы можно было import aibash.*
|
6
|
+
# Это нужно, если вы запускаете файл напрямую: python src/aibash/__main__.py
|
7
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
8
|
+
|
9
|
+
|
10
|
+
from rich.markdown import Markdown
|
11
|
+
from rich.console import Console
|
12
|
+
from rich.rule import Rule
|
13
|
+
import threading
|
14
|
+
import time
|
15
|
+
|
16
|
+
|
17
|
+
from aiebash.api_client import send_prompt
|
18
|
+
from aiebash.formatter_text import annotate_bash_blocks
|
19
|
+
from aiebash.block_runner import run_code_selection # добавлен импорт
|
20
|
+
|
21
|
+
from aiebash.settings import settings as user_settings
|
22
|
+
|
23
|
+
# нормализуем DEBUG (возможные варианты: True/False, "true"/"False", "1"/"0")
|
24
|
+
_raw_debug = user_settings.get("DEBUG")
|
25
|
+
if isinstance(_raw_debug, bool):
|
26
|
+
DEBUG = _raw_debug
|
27
|
+
else:
|
28
|
+
DEBUG = str(_raw_debug).strip().lower() in ("1", "true", "yes", "on")
|
29
|
+
CONTEXT = user_settings.get("CONTEXT")
|
30
|
+
MODEL = user_settings.get("MODEL")
|
31
|
+
API_URL = user_settings.get("API_URL")
|
32
|
+
API_KEY = user_settings.get("API_KEY")
|
33
|
+
|
34
|
+
# Флаг для остановки потока (заменён на Event)
|
35
|
+
import threading
|
36
|
+
stop_event = threading.Event()
|
37
|
+
|
38
|
+
def run_progress():
|
39
|
+
console = Console()
|
40
|
+
with console.status("[bold green]Ai печатает...[/bold green]", spinner="dots") as status:
|
41
|
+
while not stop_event.is_set():
|
42
|
+
time.sleep(0.1)
|
43
|
+
|
44
|
+
def main():
|
45
|
+
# Если нет аргументов, выводим подсказку по использованию
|
46
|
+
if len(sys.argv) < 2:
|
47
|
+
print("Использование: ai [-run] ваш запрос к ИИ без кавычек")
|
48
|
+
sys.exit(0)
|
49
|
+
|
50
|
+
console = Console()
|
51
|
+
|
52
|
+
# Проверяем ключ -run
|
53
|
+
run_mode = False
|
54
|
+
args = sys.argv[1:]
|
55
|
+
if "-run" in args:
|
56
|
+
run_mode = True
|
57
|
+
args.remove("-run")
|
58
|
+
|
59
|
+
# Собираем текст запроса из оставшихся аргументов
|
60
|
+
prompt = " ".join(args)
|
61
|
+
|
62
|
+
try:
|
63
|
+
# Запуск прогресс-бара в отдельном потоке
|
64
|
+
progress_thread = threading.Thread(target=run_progress)
|
65
|
+
progress_thread.start()
|
66
|
+
|
67
|
+
# Получаем ответ от API через новый интерфейс
|
68
|
+
answer = send_prompt(prompt, MODEL, API_URL, API_KEY, CONTEXT)
|
69
|
+
|
70
|
+
# Сигнализируем потоку прогресса остановиться
|
71
|
+
stop_event.set()
|
72
|
+
progress_thread.join() # Ждём завершения потока
|
73
|
+
|
74
|
+
# В режиме DEBUG выводим исходную (неформатированную) версию ответа
|
75
|
+
if DEBUG:
|
76
|
+
print("=== RAW RESPONSE (from send_prompt) ===")
|
77
|
+
print(answer)
|
78
|
+
print("=== /RAW RESPONSE ===\n")
|
79
|
+
|
80
|
+
# Размечаем bash-блоки и получаем список кодов
|
81
|
+
annotated_answer, code_blocks = annotate_bash_blocks(answer)
|
82
|
+
|
83
|
+
|
84
|
+
# Если включён режим выполнения и есть блоки кода — предлагаем выбрать
|
85
|
+
if run_mode and code_blocks:
|
86
|
+
console.print(Markdown(annotated_answer))
|
87
|
+
run_code_selection(console, code_blocks)
|
88
|
+
else:
|
89
|
+
console.print(Markdown(answer))
|
90
|
+
|
91
|
+
console.print(Rule("", style="green"))
|
92
|
+
|
93
|
+
except Exception as e:
|
94
|
+
# Прочие ошибки (сеть, JSON, и т.д.)
|
95
|
+
print("Ошибка:", e)
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
if __name__ == "__main__":
|
100
|
+
main()
|
aiebash/api_client.py
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
api_client — небольшой интерфейс для отправки chat-запросов к API.
|
4
|
+
Экспортирует send_chat_request (работает с messages) и удобный send_prompt.
|
5
|
+
"""
|
6
|
+
import json
|
7
|
+
import requests
|
8
|
+
import typing as t
|
9
|
+
|
10
|
+
|
11
|
+
def send_chat_request(messages: t.List[dict], model , api_url, api_key = None, timeout: int = 60) -> dict:
|
12
|
+
"""
|
13
|
+
Отправляет chat-style запрос (messages) к API и возвращает распарсенный JSON-ответ.
|
14
|
+
messages — список словарей вида {"role": "user|system|assistant", "content": "..."}.
|
15
|
+
"""
|
16
|
+
if not api_url:
|
17
|
+
raise RuntimeError("API_URL не задан в config.ini")
|
18
|
+
|
19
|
+
payload = {
|
20
|
+
"model": model,
|
21
|
+
"messages": messages,
|
22
|
+
}
|
23
|
+
|
24
|
+
headers = {"Content-Type": "application/json"}
|
25
|
+
if api_key:
|
26
|
+
headers["Authorization"] = f"Bearer {api_key}"
|
27
|
+
|
28
|
+
resp = requests.post(api_url, headers=headers, data=json.dumps(payload), timeout=timeout)
|
29
|
+
resp.raise_for_status()
|
30
|
+
data = resp.json()
|
31
|
+
|
32
|
+
return data
|
33
|
+
|
34
|
+
|
35
|
+
def send_prompt(prompt: str, model, api_url,api_key = None, system_context: str = "", timeout: int = 30) -> str:
|
36
|
+
"""
|
37
|
+
Удобная обёртка: формирует messages из system_context + user prompt,
|
38
|
+
отправляет запрос и возвращает текст ответа ассистента.
|
39
|
+
"""
|
40
|
+
messages = []
|
41
|
+
if system_context:
|
42
|
+
messages.append({"role": "system", "content": system_context})
|
43
|
+
messages.append({"role": "user", "content": prompt})
|
44
|
+
|
45
|
+
data = send_chat_request(messages, model, api_url=api_url, api_key=api_key, timeout=timeout)
|
46
|
+
# ожидаемый формат: choices -> [ { message: { content: "..." } } ]
|
47
|
+
if "choices" in data and data["choices"]:
|
48
|
+
return data["choices"][0]["message"]["content"]
|
49
|
+
raise RuntimeError("Unexpected API response format")
|
aiebash/block_runner.py
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
import subprocess
|
3
|
+
from rich.console import Console
|
4
|
+
from rich.markdown import Markdown
|
5
|
+
|
6
|
+
|
7
|
+
def run_code_selection(console: Console, code_blocks: list):
|
8
|
+
"""
|
9
|
+
Интерактивный цикл: спрашивает номер блока, выводит его содержимое и при выборе выполняет его.
|
10
|
+
Завершается при вводе 0, q, exit или при Ctrl+C / EOF.
|
11
|
+
"""
|
12
|
+
try:
|
13
|
+
while True:
|
14
|
+
choice = console.input("[blue]\nВведите номер блока кода для запуска (0 — выход): [/blue]").strip()
|
15
|
+
if choice.lower() in ("0", "q", "exit"):
|
16
|
+
console.print("Выход.")
|
17
|
+
break
|
18
|
+
if not choice.isdigit():
|
19
|
+
console.print("[red]Введите число или 0 для выхода.[/red]")
|
20
|
+
continue
|
21
|
+
idx = int(choice)
|
22
|
+
if idx < 1 or idx > len(code_blocks):
|
23
|
+
console.print(f"[red]Неверный номер: у вас {len(code_blocks)} блоков. Попробуйте снова.[/red]")
|
24
|
+
continue
|
25
|
+
console.print(f"\n>>> Выполняем блок #{idx}:\n", style="blue")
|
26
|
+
console.print(code_blocks[idx - 1])
|
27
|
+
# Выполнение — риск: выполняется произвольный код из ответа ИИ
|
28
|
+
subprocess.run(code_blocks[idx - 1], shell=True)
|
29
|
+
except (EOFError, KeyboardInterrupt):
|
30
|
+
console.print("\nВыход.")
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import re
|
2
|
+
|
3
|
+
def annotate_bash_blocks(md_text):
|
4
|
+
"""
|
5
|
+
Находит fenced code blocks с языком bash, собирает их содержимое в список
|
6
|
+
и добавляет над каждым блоком строку-метку вида "**Блок кода bash [#{counter}]**".
|
7
|
+
Возвращает (annotated_md, list_of_code_strings).
|
8
|
+
"""
|
9
|
+
code_blocks = []
|
10
|
+
counter = 0
|
11
|
+
|
12
|
+
pattern = re.compile(r"```bash[^\n]*\n(.*?)```", re.DOTALL | re.IGNORECASE)
|
13
|
+
|
14
|
+
def repl(m):
|
15
|
+
nonlocal counter
|
16
|
+
counter += 1
|
17
|
+
code = m.group(1).rstrip("\n")
|
18
|
+
code_blocks.append(code)
|
19
|
+
label = f"\n**Блок кода bash [#{counter}]**\n"
|
20
|
+
return label + "```bash\n" + code + "\n```"
|
21
|
+
|
22
|
+
annotated = pattern.sub(repl, md_text)
|
23
|
+
return annotated, code_blocks
|
aiebash/settings.py
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
|
2
|
+
from pathlib import Path
|
3
|
+
import configparser
|
4
|
+
from platformdirs import user_config_dir
|
5
|
+
|
6
|
+
APP_NAME = "ai-bash"
|
7
|
+
|
8
|
+
# Дефолтные значения хранятся в словаре
|
9
|
+
DEFAULTS = {
|
10
|
+
"API_URL": "https://openai-proxy.andrey-bch-1976.workers.dev/v1/chat/completions", # Прокси для OpenAI API
|
11
|
+
"API_KEY": "", # Ключ API (если нужен)
|
12
|
+
"MODEL": "gpt-4o-mini", # Модель по умолчанию
|
13
|
+
"CONTEXT": ( # Контекст по умолчанию
|
14
|
+
"Ты профессиональный системный администратор, помощник по Linux. "
|
15
|
+
"Пользователь - новичок в Linux, помоги ему. "
|
16
|
+
"Система Ubuntu, оболочка $SHELL. "
|
17
|
+
"При ответе учитывай, что пользователь работает в терминале Bash."
|
18
|
+
"Отвечай всегда на русском языке. "
|
19
|
+
"Ты разговариваешь с пользователем в терминале. "
|
20
|
+
),
|
21
|
+
"DEBUG": False # Включить отладочную информацию
|
22
|
+
}
|
23
|
+
|
24
|
+
# Где хранится INI-файл
|
25
|
+
# Ubuntu → ~/.config/ai-bash/config.ini
|
26
|
+
# Windows → C:\Users\<user>\AppData\Local\ai-bash\ai-bash\config.ini
|
27
|
+
|
28
|
+
class Settings:
|
29
|
+
def __init__(self):
|
30
|
+
# Кроссплатформенная директория конфигов
|
31
|
+
self.config_dir = Path(user_config_dir(APP_NAME))
|
32
|
+
self.config_dir.mkdir(parents=True, exist_ok=True)
|
33
|
+
|
34
|
+
self.config_file = self.config_dir / "config.ini"
|
35
|
+
self.config = configparser.ConfigParser()
|
36
|
+
|
37
|
+
# Если файл есть — читаем, иначе создаём с дефолтами
|
38
|
+
if self.config_file.exists():
|
39
|
+
self.config.read(self.config_file)
|
40
|
+
if "settings" not in self.config:
|
41
|
+
self.config["settings"] = DEFAULTS
|
42
|
+
self._save()
|
43
|
+
else:
|
44
|
+
self.config["settings"] = DEFAULTS
|
45
|
+
self._save()
|
46
|
+
print(f"[INFO] Создан новый конфиг: {self.config_file}")
|
47
|
+
|
48
|
+
def _save(self):
|
49
|
+
with open(self.config_file, "w") as f:
|
50
|
+
self.config.write(f)
|
51
|
+
|
52
|
+
# Получение значений
|
53
|
+
def get(self, key):
|
54
|
+
return self.config["settings"].get(key, DEFAULTS.get(key))
|
55
|
+
|
56
|
+
# Изменение значений
|
57
|
+
def set(self, key, value):
|
58
|
+
self.config["settings"][key] = str(value)
|
59
|
+
self._save()
|
60
|
+
|
61
|
+
|
62
|
+
# Глобальный объект настроек
|
63
|
+
settings = Settings()
|
64
|
+
|
65
|
+
|