diff --git a/USAGE_GUIDE.md b/USAGE_GUIDE.md new file mode 100644 index 0000000..f75b9fb --- /dev/null +++ b/USAGE_GUIDE.md @@ -0,0 +1,235 @@ +# Руководство по использованию (USAGE_GUIDE) + +## Что это + +Linux Command GPT (`lcg`) преобразует описание на естественном языке в готовую Linux‑команду. Инструмент поддерживает сменные провайдеры LLM (Ollama или Proxy), управление системными промптами, историю за сессию, сохранение результатов и интерактивные действия над сгенерированной командой. + +## Требования + +- Установленный Go (для сборки из исходников) или готовый бинарник. +- Для функции «скопировать в буфер обмена»: установите `xclip` или `xsel`. + +```bash +# Debian/Ubuntu +sudo apt-get install xclip +# или +sudo apt-get install xsel +``` + +## Установка + +Сборка из исходников: + +```bash +git clone --depth 1 https://github.com/asrul10/linux-command-gpt.git ~/.linux-command-gpt +cd ~/.linux-command-gpt +go build -o lcg + +# Добавьте бинарник в PATH +ln -s ~/.linux-command-gpt/lcg ~/.local/bin +``` + +Или скачайте готовый бинарник из раздела релизов. + +## Быстрый старт + +Простой запрос: + +```bash +lcg "хочу извлечь файл linux-command-gpt.tar.gz" +``` + +Смешанный ввод: часть из файла, часть — текстом: + +```bash +lcg --file /path/to/context.txt "хочу вывести список директорий с помощью ls" +``` + +После генерации вы увидите: + +```text +🤖 Запрос: <ваше описание> +✅ Выполнено за X.XX сек + +📋 Команда: + <сгенерированная команда> + +Действия: (c)копировать, (s)сохранить, (r)перегенерировать, (e)выполнить, (n)ничего: +``` + +## Переменные окружения + +Можно настроить поведение без изменения командной строки. + +| Переменная | Значение по умолчанию | Назначение | +| --- | --- | --- | +| `LCG_HOST` | `http://192.168.87.108:11434/` | Базовый URL API провайдера (для Ollama поставьте, например, `http://localhost:11434/`). | +| `LCG_COMPLETIONS_PATH` | `api/chat` | Относительный путь эндпоинта для Ollama. | +| `LCG_MODEL` | `codegeex4` | Имя модели у выбранного провайдера. | +| `LCG_PROMPT` | См. значение в коде | Содержимое системного промпта по умолчанию. | +| `LCG_API_KEY_FILE` | `.openai_api_key` | Файл с API‑ключом (для Ollama/Proxy не требуется). | +| `LCG_RESULT_FOLDER` | `$(pwd)/gpt_results` | Папка для сохранения результатов. | +| `LCG_PROVIDER` | `ollama` | Тип провайдера: `ollama` или `proxy`. | +| `LCG_JWT_TOKEN` | пусто | JWT токен для `proxy` провайдера (альтернатива — файл `~/.proxy_jwt_token`). | +| `LCG_PROMPT_ID` | `1` | ID системного промпта по умолчанию. | +| `LCG_TIMEOUT` | `120` | Таймаут запроса в секундах. | + +Примеры настройки: + +```bash +# Ollama +export LCG_PROVIDER=ollama +export LCG_HOST=http://localhost:11434/ +export LCG_MODEL=codegeex4 + +# Proxy +export LCG_PROVIDER=proxy +export LCG_HOST=http://localhost:8080 +export LCG_MODEL=GigaChat-2 +export LCG_JWT_TOKEN=your_jwt_token_here +``` + +## Базовый синтаксис + +```bash +lcg [глобальные опции] <описание команды> +``` + +Глобальные опции: + +- `--file, -f string` — прочитать часть запроса из файла и добавить к описанию. +- `--sys, -s string` — системный промпт (содержимое или ID как строка). Если не задан, используется `--prompt-id` или `LCG_PROMPT`. +- `--prompt-id, --pid int` — ID системного промпта (1–5 для стандартных, либо ваш кастомный ID). +- `--timeout, -t int` — таймаут запроса в секундах (по умолчанию 120). +- `--version, -v` — вывести версию. +- `--help, -h` — помощь. + +## Подкоманды + +- `lcg update-key` (`-u`): обновить API‑ключ. Для `ollama` и `proxy` не требуется — команда сообщит, что ключ не нужен. +- `lcg delete-key` (`-d`): удалить API‑ключ (не требуется для `ollama`/`proxy`). +- `lcg update-jwt` (`-j`): обновить JWT для `proxy`. Токен будет сохранён в `~/.proxy_jwt_token` (права `0600`). +- `lcg delete-jwt` (`-dj`): удалить JWT файл для `proxy`. +- `lcg models` (`-m`): показать доступные модели у текущего провайдера. +- `lcg health` (`-he`): проверить доступность API провайдера. +- `lcg config` (`-co`): показать текущую конфигурацию и состояние JWT. +- `lcg history` (`-hist`): показать историю запросов за текущий запуск (до 100 записей, не сохраняется между запусками). +- `lcg prompts ...` (`-p`): управление системными промптами: + - `lcg prompts list` (`-l`) — список всех промптов. + - `lcg prompts add` (`-a`) — добавить пользовательский промпт (по шагам в интерактиве). + - `lcg prompts delete ` (`-d`) — удалить пользовательский промпт по ID (>5). +- `lcg test-prompt <описание>` (`-tp`): показать детали выбранного системного промпта и протестировать его на заданном описании. + +## Провайдеры + +### Ollama (`LCG_PROVIDER=ollama`) + +- Требуется запущенный Ollama API (`LCG_HOST`, например `http://localhost:11434/`). +- `models`, `health` и генерация используют REST Ollama (`/api/tags`, `/api/chat`). +- API‑ключ не нужен. + +### Proxy (`LCG_PROVIDER=proxy`) + +- Требуется доступ к прокси‑серверу (`LCG_HOST`) и JWT (`LCG_JWT_TOKEN` или файл `~/.proxy_jwt_token`). +- Основные эндпоинты: `/api/v1/protected/sberchat/chat` и `/api/v1/protected/sberchat/health`. +- Команды `update-jwt`/`delete-jwt` помогают управлять токеном локально. + +## Системные промпты + +Встроенные (ID 1–5): + +| ID | Name | Описание | +| --- | --- | --- | +| 1 | linux-command | «Ответь только Linux‑командой, без форматирования и объяснений». | +| 2 | linux-command-with-explanation | Сгенерируй команду и кратко объясни, что она делает (формат: COMMAND: explanation). | +| 3 | linux-command-safe | Безопасные команды (без потери данных). Вывод — только команда. | +| 4 | linux-command-verbose | Команда с подробными объяснениями флагов и альтернатив. | +| 5 | linux-command-simple | Простые команды, избегать сложных опций. | + +Пользовательские промпты сохраняются в `~/.lcg_prompts.json` и доступны между запусками. + +## Сохранение результатов + +При выборе действия `s` ответ сохраняется в `LCG_RESULT_FOLDER` (по умолчанию: `./gpt_results`) в файл вида: + +```text +gpt_request__YYYY-MM-DD_HH-MM-SS.md +``` + +Структура файла: + +- `## Prompt:` — ваш запрос (включая системный промпт). +- `## Response:` — полученный ответ. + +## Выполнение сгенерированной команды + +Действие `e` запустит команду через `bash -c`. Перед запуском потребуется подтверждение `y/yes`. Всегда проверяйте команду вручную, особенно при операциях с файлами и сетью. + +## Примеры + +1. Базовый запрос с Ollama: + +```bash +export LCG_PROVIDER=ollama +export LCG_HOST=http://localhost:11434/ +export LCG_MODEL=codegeex4 + +lcg "хочу извлечь linux-command-gpt.tar.gz" +``` + +1. Полный ответ от LLM (пример настройки): + +```bash +LCG_PROMPT='Provide full response' LCG_MODEL=codellama:13b \ + lcg 'i need bash script to execute command by ssh on array of hosts' +``` + +1. Proxy‑провайдер: + +```bash +export LCG_PROVIDER=proxy +export LCG_HOST=http://localhost:8080 +export LCG_MODEL=GigaChat-2 +export LCG_JWT_TOKEN=your_jwt_token_here + +lcg "I want to extract linux-command-gpt.tar.gz file" + +lcg health +lcg config +lcg update-jwt +``` + +1. Работа с файлами и промптами: + +```bash +lcg --file ./context.txt "сгенерируй команду jq для выборки поля name" +lcg --prompt-id 2 "удали все *.tmp в текущем каталоге" +lcg --sys 1 "показать размер каталога в человеко‑читаемом виде" +``` + +1. Диагностика и модели: + +```bash +lcg health +lcg models +``` + +## История + +`lcg history` выводит историю текущего процесса (не сохраняется между запусками, максимум 100 записей): + +```bash +lcg history +``` + +## Типичные проблемы + +- Нет ответа/таймаут: увеличьте `--timeout` или `LCG_TIMEOUT`, проверьте `LCG_HOST` и сетевую доступность. +- `health` падает: проверьте, что провайдер запущен и URL верный; для `proxy` — что JWT валиден (`lcg config`). +- Копирование не работает: установите `xclip` или `xsel`. +- Нет допуска к папке результатов: настройте `LCG_RESULT_FOLDER` или права доступа. +- Для `ollama`/`proxy` API‑ключ не нужен; команды `update-key`/`delete-key` просто уведомят об этом. + +## Лицензия и исходники + +См. README и репозиторий проекта. Предложения и баг‑репорты приветствуются в Issues. diff --git a/shell-code/pre-release.sh b/shell-code/pre-release.sh new file mode 100644 index 0000000..3aefe80 --- /dev/null +++ b/shell-code/pre-release.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +go-ansible-vault -i shell-code/build.env -a get -m GITHUB_TOKEN > /tmp/source && source /tmp/source + +GITHUB_TOKEN=$GITHUB_TOKEN python3 shell-code/release.py \ No newline at end of file diff --git a/shell-code/release.py b/shell-code/release.py new file mode 100644 index 0000000..d1091d9 --- /dev/null +++ b/shell-code/release.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python3 +""" +Скрипт для создания релиза на GitHub +Использование: GITHUB_TOKEN=your_token python3 release.py +""" + +import os +import sys +import json +import requests +from pathlib import Path + +# Цвета для вывода +class Colors: + RED = '\033[0;31m' + GREEN = '\033[0;32m' + YELLOW = '\033[1;33m' + BLUE = '\033[0;34m' + NC = '\033[0m' # No Color + +def log(message): + print(f"{Colors.GREEN}[INFO]{Colors.NC} {message}") + +def error(message): + print(f"{Colors.RED}[ERROR]{Colors.NC} {message}", file=sys.stderr) + +def warn(message): + print(f"{Colors.YELLOW}[WARN]{Colors.NC} {message}") + +def debug(message): + print(f"{Colors.BLUE}[DEBUG]{Colors.NC} {message}") + +# Конфигурация +REPO = "direct-dev-ru/go-lcg" +VERSION_FILE = "VERSION.txt" +BINARIES_DIR = "binaries-for-upload" + +def check_environment(): + """Проверка переменных окружения""" + token = os.getenv('GITHUB_TOKEN') + if not token: + error("GITHUB_TOKEN не установлен") + sys.exit(1) + log(f"GITHUB_TOKEN установлен (длина: {len(token)} символов)") + return token + +def get_version(): + """Получение версии из файла""" + version_file = Path(VERSION_FILE) + if not version_file.exists(): + error(f"Файл {VERSION_FILE} не найден") + sys.exit(1) + + version = version_file.read_text().strip() + tag = f"lcg.{version}" + log(f"Версия: {version}") + log(f"Тег: {tag}") + return tag + +def check_files(): + """Проверка файлов для загрузки""" + binaries_path = Path(BINARIES_DIR) + if not binaries_path.exists(): + error(f"Директория {BINARIES_DIR} не найдена") + sys.exit(1) + + files = list(binaries_path.glob("*")) + files = [f for f in files if f.is_file()] + + if not files: + error(f"В директории {BINARIES_DIR} нет файлов") + sys.exit(1) + + log(f"Найдено файлов: {len(files)}") + for file in files: + log(f" - {file.name} ({file.stat().st_size} байт)") + + return files + +def create_github_session(token): + """Создание сессии для GitHub API""" + session = requests.Session() + session.headers.update({ + 'Authorization': f'token {token}', + 'Accept': 'application/vnd.github.v3+json', + 'User-Agent': 'release-script' + }) + return session + +def check_existing_release(session, tag): + """Проверка существующего релиза""" + log("Проверяем существующий релиз...") + url = f"https://api.github.com/repos/{REPO}/releases/tags/{tag}" + + response = session.get(url) + if response.status_code == 200: + release_data = response.json() + log(f"Реліз {tag} уже существует") + return release_data + elif response.status_code == 404: + log(f"Реліз {tag} не найден, создаем новый") + return None + else: + error(f"Ошибка проверки релиза: {response.status_code}") + debug(f"Ответ: {response.text}") + sys.exit(1) + +def create_release(session, tag): + """Создание нового релиза""" + log(f"Создаем новый релиз {tag}...") + + data = { + "tag_name": tag, + "name": tag, + "body": f"Release {tag}", + "draft": False, + "prerelease": False + } + + url = f"https://api.github.com/repos/{REPO}/releases" + response = session.post(url, json=data) + + if response.status_code == 201: + release_data = response.json() + log("Реліз создан успешно") + return release_data + else: + error(f"Ошибка создания релиза: {response.status_code}") + debug(f"Ответ: {response.text}") + sys.exit(1) + +def upload_file(session, upload_url, file_path): + """Загрузка файла в релиз""" + filename = file_path.name + log(f"Загружаем: {filename}") + + # Убираем {?name,label} из URL + upload_url = upload_url.replace("{?name,label}", "") + + with open(file_path, 'rb') as f: + headers = {'Content-Type': 'application/octet-stream'} + params = {'name': filename} + + response = session.post( + upload_url, + data=f, + headers=headers, + params=params + ) + + if response.status_code == 201: + log(f"✓ {filename} загружен") + return True + else: + error(f"Ошибка загрузки {filename}: {response.status_code}") + debug(f"Ответ: {response.text}") + return False + +def main(): + """Основная функция""" + log("=== НАЧАЛО РАБОТЫ СКРИПТА ===") + + # Проверки + token = check_environment() + tag = get_version() + files = check_files() + + # Создание сессии + session = create_github_session(token) + + # Проверка/создание релиза + release = check_existing_release(session, tag) + if not release: + release = create_release(session, tag) + + # Получение URL для загрузки + upload_url = release['upload_url'] + log(f"Upload URL: {upload_url}") + + # Загрузка файлов + log("=== ЗАГРУЗКА ФАЙЛОВ ===") + uploaded = 0 + failed = 0 + + for file_path in files: + if upload_file(session, upload_url, file_path): + uploaded += 1 + else: + failed += 1 + + # Результат + log("=== РЕЗУЛЬТАТ ===") + log(f"Успешно загружено: {uploaded}") + if failed > 0: + warn(f"Ошибок: {failed}") + else: + log("Все файлы загружены успешно!") + + log(f"Реліз доступен: https://github.com/{REPO}/releases/tag/{tag}") + log("=== СКРИПТ ЗАВЕРШЕН ===") + +if __name__ == "__main__": + main() diff --git a/shell-code/release.sh b/shell-code/release.sh new file mode 100644 index 0000000..49acefb --- /dev/null +++ b/shell-code/release.sh @@ -0,0 +1,134 @@ +#!/bin/bash + +# Простой скрипт для создания релиза на GitHub +# Использование: GITHUB_TOKEN=your_token ./release.sh + +set -e + +# Цвета +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +# Функции логирования +log() { echo -e "${GREEN}[INFO]${NC} $1"; } +error() { echo -e "${RED}[ERROR]${NC} $1" >&2; } +warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } +debug() { echo -e "${BLUE}[DEBUG]${NC} $1"; } + +# Конфигурация +REPO="direct-dev-ru/go-lcg" +VERSION_FILE="VERSION.txt" +BINARIES_DIR="binaries-for-upload" + +# Проверки +if [[ -z "$GITHUB_TOKEN" ]]; then + error "GITHUB_TOKEN не установлен" + exit 1 +fi + +if [[ ! -f "$VERSION_FILE" ]]; then + error "Файл $VERSION_FILE не найден" + exit 1 +fi + +if [[ ! -d "$BINARIES_DIR" ]]; then + error "Директория $BINARIES_DIR не найдена" + exit 1 +fi + +# Получение версии +VERSION=$(cat "$VERSION_FILE" | tr -d ' \t\n\r') +TAG="lcg.$VERSION" + +log "Версия: $VERSION" +log "Тег: $TAG" + +# Проверяем, существует ли уже релиз +log "Проверяем существующий релиз..." +EXISTING_RELEASE=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \ + "https://api.github.com/repos/$REPO/releases/tags/$TAG") + +if echo "$EXISTING_RELEASE" | grep -q '"id":'; then + log "Реліз $TAG уже существует, получаем upload_url..." + UPLOAD_URL=$(echo "$EXISTING_RELEASE" | grep '"upload_url"' | cut -d'"' -f4 | sed 's/{?name,label}//') +else + log "Создаем новый релиз $TAG..." + + # Создаем релиз + RELEASE_DATA="{\"tag_name\":\"$TAG\",\"name\":\"$TAG\",\"body\":\"Release $TAG\"}" + + RELEASE_RESPONSE=$(curl -s -X POST \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.github.com/repos/$REPO/releases" \ + -d "$RELEASE_DATA") + + if echo "$RELEASE_RESPONSE" | grep -q '"message"'; then + error "Ошибка создания релиза:" + echo "$RELEASE_RESPONSE" | grep '"message"' | cut -d'"' -f4 + exit 1 + fi + + UPLOAD_URL=$(echo "$RELEASE_RESPONSE" | grep '"upload_url"' | cut -d'"' -f4 | sed 's/{?name,label}//') + log "Реліз создан успешно" +fi + +if [[ -z "$UPLOAD_URL" ]]; then + error "Не удалось получить upload_url" + exit 1 +fi + +log "Upload URL: $UPLOAD_URL" + +# Проверяем файлы в директории +log "Проверяем файлы в директории $BINARIES_DIR:" +ls -la "$BINARIES_DIR" + +# Загружаем файлы +log "Загружаем файлы..." +UPLOADED=0 +FAILED=0 + +# Простой цикл по всем файлам в директории +for file in "$BINARIES_DIR"/*; do + if [[ -f "$file" ]]; then + filename=$(basename "$file") + log "Обрабатываем файл: $file" + debug "Имя файла: $filename" + + log "Загружаем: $filename" + + response=$(curl -s -X POST \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Content-Type: application/octet-stream" \ + "$UPLOAD_URL?name=$filename" \ + --data-binary @"$file") + + debug "Ответ API: $response" + + if echo "$response" | grep -q '"message"'; then + error "Ошибка загрузки $filename:" + echo "$response" | grep '"message"' | cut -d'"' -f4 + ((FAILED++)) + else + log "✓ $filename загружен" + ((UPLOADED++)) + fi + else + warn "Пропускаем не-файл: $file" + fi +done + +# Результат +log "=== РЕЗУЛЬТАТ ===" +log "Успешно загружено: $UPLOADED" +if [[ $FAILED -gt 0 ]]; then + warn "Ошибок: $FAILED" +else + log "Все файлы загружены успешно!" +fi + +log "Реліз доступен: https://github.com/$REPO/releases/tag/$TAG" diff --git a/shell-code/test_api.py b/shell-code/test_api.py new file mode 100644 index 0000000..ed071ab --- /dev/null +++ b/shell-code/test_api.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +""" +Скрипт для тестирования GitHub API +Использование: GITHUB_TOKEN=your_token python3 test_api.py +""" + +import os +import sys +import requests + +# Цвета +class Colors: + RED = '\033[0;31m' + GREEN = '\033[0;32m' + YELLOW = '\033[1;33m' + NC = '\033[0m' + +def log(message): + print(f"{Colors.GREEN}[INFO]{Colors.NC} {message}") + +def error(message): + print(f"{Colors.RED}[ERROR]{Colors.NC} {message}") + +def main(): + REPO = "direct-dev-ru/go-lcg" + + token = os.getenv('GITHUB_TOKEN') + if not token: + error("GITHUB_TOKEN не установлен") + sys.exit(1) + + session = requests.Session() + session.headers.update({ + 'Authorization': f'token {token}', + 'Accept': 'application/vnd.github.v3+json' + }) + + print("=== ТЕСТИРОВАНИЕ GITHUB API ===") + + # Тест 1: Проверка доступа к репозиторию + print("1. Проверка доступа к репозиторию...") + response = session.get(f"https://api.github.com/repos/{REPO}") + + if response.status_code == 200: + repo_data = response.json() + print(f"✅ Доступ к репозиторию есть") + print(f" Репозиторий: {repo_data['full_name']}") + print(f" Описание: {repo_data.get('description', 'Нет описания')}") + else: + print(f"❌ Ошибка доступа: {response.status_code}") + print(f" Ответ: {response.text}") + + # Тест 2: Проверка прав + print("\n2. Проверка прав...") + if response.status_code == 200: + permissions = repo_data.get('permissions', {}) + if permissions.get('admin'): + print("✅ Есть права администратора") + elif permissions.get('push'): + print("✅ Есть права на запись") + else: + print("❌ Недостаточно прав для создания релизов") + + # Тест 3: Последние релизы + print("\n3. Последние релизы:") + releases_response = session.get(f"https://api.github.com/repos/{REPO}/releases") + + if releases_response.status_code == 200: + releases = releases_response.json() + if releases: + for release in releases[:5]: + print(f" - {release['tag_name']} ({release['name']})") + else: + print(" Релизов пока нет") + else: + print(f" Ошибка получения релизов: {releases_response.status_code}") + + print("\n=== ТЕСТ ЗАВЕРШЕН ===") + +if __name__ == "__main__": + main() diff --git a/shell-code/upload-release.sh b/shell-code/upload-release.sh deleted file mode 100644 index c8b4f1b..0000000 --- a/shell-code/upload-release.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/bash - -# Variables -VERSION_FILE="VERSION.txt" - -GITHUB_TOKEN="${GITHUB_TOKEN}" # Replace with your GitHub token - -REPO="direct-dev-ru/binaries" # Replace with your GitHub username/repo - -TAG=lcg.$(cat "$VERSION_FILE") - -echo TAG: $TAG - -RELEASE_DIR="/home/su/projects/golang/linux-command-gpt/binaries-for-upload" - -body="{\"tag_name\":\"${TAG}\", \"target_commitish\":\"main\", \"name\":\"${TAG}\", \ - \"body\":\"${TAG}\", \"draft\":false, \"prerelease\":false, \"generate_release_notes\":false}" - -echo BODY: $body - -response=$(curl -L -X POST \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: Bearer ${GITHUB_TOKEN}" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - https://api.github.com/repos/direct-dev-ru/binaries/releases \ - -d $body) - -echo $response - -# Extract the upload URL from the response -upload_url=$(echo "$response" | jq -r '.upload_url' | sed "s/{?name,label}//") - -# Check if the release was created successfully -if [[ "$response" == *"Not Found"* ]]; then - echo "Error: Repository not found or invalid token." - exit 1 -fi - -# Upload each binary file -for file in "$RELEASE_DIR"/*; do - if [[ -f "$file" ]]; then - filename=$(basename "$file") - echo "Uploading $filename..." - response=$(curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \ - -H "Content-Type: application/octet-stream" \ - "$upload_url?name=$filename" \ - --data-binary @"$file") - echo $response - fi -done - -echo "All binaries uploaded successfully."