From 83140ded6669d5bcb32fa702ffbfdc3a74962f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Fri, 22 May 2026 21:02:34 +0400 Subject: [PATCH] =?UTF-8?q?germany:=20=D1=83=D0=B4=D0=B0=D0=BB=D1=91=D0=BD?= =?UTF-8?q?=20=D0=BA=D1=83=D0=B1=D0=B5=D1=80-=D0=BD=D0=B0=D1=81=D0=BB?= =?UTF-8?q?=D0=B5=D0=B4=D0=B8=D0=B5,=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20.github=20=D0=BF=D0=BE=D0=B4=20?= =?UTF-8?q?=D0=B3=D0=B5=D1=80=D0=BC=D0=B0=D0=BD=D1=81=D0=BA=D1=83=D1=8E=20?= =?UTF-8?q?=D0=92=D0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/copilot-instructions.md | 89 +++++----------------- .github/pravila.md | 130 +++++++------------------------- k8s/deployment.yaml | 45 ----------- k8s/ingress.yaml | 28 ------- nginx.conf | 17 ----- ws_server.py | 75 ------------------ 6 files changed, 45 insertions(+), 339 deletions(-) delete mode 100644 k8s/deployment.yaml delete mode 100644 k8s/ingress.yaml delete mode 100644 nginx.conf delete mode 100644 ws_server.py diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 1410bda..899dc5e 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,75 +1,20 @@ -# Правила +# Lyngvo — Copilot Instructions -## ⛔ ОТВЕЧАТЬ КРАТКО — АБСОЛЮТНОЕ ПРАВИЛО -- Вопрос → короткий ответ → СТОП. -- Ничего лишнего. -- Код — только по запросу. +## Ключевые правила +1. Отвечать кратко. Вопрос = СТОП. +2. Не трогать рабочий код без «делай». +3. После любой правки — VERSION++ в js/main.js. +4. Деплой: `cd ~/lang && bash deploy_lang.sh` +5. Git только локально: add + commit + push. -## ⛔⛔⛔ ВОПРОС = СТОП +## Сервер +- Германия, Vultr, 95.179.252.111 +- SSH: `ssh -i ~/.ssh/vultr_openssh root@95.179.252.111` +- Сайт: https://lang.kube5s.ru +- Nginx + Let's Encrypt + Groq прокси (gunicorn :8765) -**Если в сообщении есть вопрос в ЛЮБОЙ форме** ("так ?", "верно ?", "почему ?", "как ?", "так же ?" и т.д.): -1. Только ответить на вопрос -2. ОСТАНОВИТЬСЯ -3. ЖДАТЬ следующей команды -**ЗАПРЕЩЕНО** начинать работу, писать код, запускать команды — без явного "делай". - -## ⛔⛔⛔ НЕ ТРОГАТЬ РАБОЧИЙ КОД — АБСОЛЮТНЫЙ ЗАПРЕТ НАВСЕГДА - -**НИКАКИХ самодеятельных изменений рабочего кода:** -- Никаких "оптимизаций", "улучшений", "рефакторинга" без команды -- Никаких новых фич без явного разрешения -- Никаких helm upgrade, kubectl patch и прочих инфраструктурных изменений без команды -- Перед ЛЮБЫМ изменением рабочего кода — объяснить ЗАЧЕМ и ждать "делай" - -**ПРЕЦЕДЕНТЫ:** -- v1.3.49: убрали mode toggle в edit modal "для улучшения" → сломали рабочий редактор -- helm upgrade попытка → сломал JWT secret router → 401 у всех функций пользователей - -1. Не трогать рабочий код без явного указания. - -2. Файлы редактируются локально: - ~/lang - - После ЛЮБЫХ изменений ОБЯЗАТЕЛЬНО синхронизировать на ВМ командой: - rsync -az \ - -e "ssh -i ~/.ssh/naeel_vm_id_ed25519 -o StrictHostKeyChecking=no -o ConnectTimeout=10" \ - ~/lang/ \ - naeel@5.172.178.213:~/terra/lang/ - - -3. Git (add/commit/push) выполнять ЛОКАЛЬНО в ~/lang -4. Docker, kubectl и другие инфраструктурные команды — только через SSH на ВМ: - ssh -i ~/.ssh/naeel_vm_id_ed25519 -o StrictHostKeyChecking=no -o ConnectTimeout=10 naeel@5.172.178.213 'КОМАНДА' - - - не выполнять инфраструктурные команды локально - - не открывать интерактивные сессии - - не делать цепочки без необходимости - -4. Перед запуском команд ОБЯЗАТЕЛЬНО убедиться, что синхронизация выполнена. - -5. ЗАПРЕЩЕНО: - - откатывать код - - менять версии - - ломать рабочее состояние - -6. После каждого исправления: - - git add/commit ЛОКАЛЬНО - - затем синхронизация (rsync) на ВМ - -7. ~/lang/test-results/ - -## ⛔⛔⛔ ДЕЛАТЬ ТОЛЬКО ЧТО ПРЯМО ПРИКАЗАНО - -**АБСОЛЮТНЫЙ ЗАПРЕТ на додумывание:** -- Не расширять масштаб работы -- Не выполнять "логичные следующие шаги" -- Не инициировать дополнительные операции -- Не делать ничего кроме того что сказано - -**Пример (2026-05-01):** -- Приказано: "собери" -- Сделано: ✓ собрал образы v1.3.17 и v0.1.2 -- СТОП — жду команды дальше -- **ЗАПРЕЩЕНО:** обновлять манифесты, заливать образы, применять на кластер, запускать тесты - -**Исключение:** только если приказ явно включает цепочку ("собери И залей И тесты") +## Стек +- Статика: HTML/CSS/JS (без фреймворков) +- Сборка: build.sh склеивает CSS/JS в dist/index.html +- Деплой: scp dist/index.html → /var/www/lyngvo/ +- API: Groq Whisper + Llama через /openai/ прокси diff --git a/.github/pravila.md b/.github/pravila.md index f27047a..6c56e4b 100644 --- a/.github/pravila.md +++ b/.github/pravila.md @@ -1,112 +1,38 @@ -# Правила работы агента +# Правила работы агента — Lyngvo (германская ВМ) -## ⛔⛔⛔ ВЕРСИЯ — УВЕЛИЧИВАТЬ ПОСЛЕ ЛЮБОЙ ПРАВКИ +## ⛔ ВЕРСИЯ — УВЕЛИЧИВАТЬ ПОСЛЕ ЛЮБОЙ ПРАВКИ +После ЛЮБОГО изменения кода — VERSION++ в js/main.js. -**После ЛЮБОГО изменения кода (HTML/JS/CSS) — обязательно VERSION++ в `js/main.js`.** -Без исключений. Даже если правка в одну строку. +## ⛔ ОТВЕЧАТЬ КРАТКО — вопрос = СТОП -## ⛔⛔⛔ DOCKER — ОБЯЗАТЕЛЬНЫЙ ПОРЯДОК ПЕРЕД КАЖДЫМ BUILD +## ⛔ НЕ ТРОГАТЬ РАБОЧИЙ КОД без явного «делай» -1. УВЕЛИЧИТЬ ТЕГ в `deploy/lang.yaml` (vX.Y.Z → vX.Y.Z+1) -2. rsync на ВМ -3. ПРОВЕРИТЬ что файлы на ВМ новые (grep ключевой строки) -4. docker build с НОВЫМ тегом -5. docker push с НОВЫМ тегом -6. kubectl apply (не rollout restart — apply подтягивает новый тег) +## ⛔ ДЕЛАТЬ ТОЛЬКО ЧТО ПРИКАЗАНО -**НИКОГДА не делать `docker build` со старым тегом — под не перетянет образ (imagePullPolicy: IfNotPresent)** +## Файловая система +- Редактировать локально: ~/lang +- Git: только локально. add + commit + push. Остальное запрещено. -## Файловая система (актуально) - -1. Все файлы редактируются локально: `~/lang` -2. После любых изменений — обязательно rsync на ВМ: - rsync -az \ - -e "ssh -i ~/.ssh/naeel_vm_id_ed25519 -o StrictHostKeyChecking=no -o ConnectTimeout=10" \ - ~/lang/ \ - naeel@5.172.178.213:~/terra/lang/ - -3. Git (add/commit/push) выполнять ЛОКАЛЬНО в ~/lang -4. Docker, kubectl и другие инфраструктурные команды — только через SSH на ВМ -5. Перед запуском любой команды на ВМ обязательно убедиться, что синхронизация (rsync) выполнена -6. SCP, sshfs, remote_dev и маунты больше НЕ используются -7. Только rsync для синхронизации - -Пример: -1. Редактируешь локально -2. rsync на ВМ -3. Выполняешь команды через SSH на ВМ - -## SSH - -Все команды — только через SSH на ВМ. Локально — только читать и редактировать файлы. - -```bash -ssh -i ~/.ssh/naeel_vm_id_ed25519 -o StrictHostKeyChecking=no -o ConnectTimeout=10 naeel@5.172.178.213 'КОМАНДА' +## Деплой ``` - -Запрещено локально: `go`, `docker`, `kubectl`, `helm`, `terraform`, `curl/wget`, `git push/pull`, любые скрипты проекта. - -## Документация - -- `doc/thinking/` — лог рассуждений агента (обязательно) -- `doc/progress.md` — трекер задач -- Старые файлы `doc/` не перезаписывать — новое в новых файлах с датой - -## Git - -⛔⛔⛔ АБСОЛЮТНОЕ ПРАВИЛО: -- Git — ТОЛЬКО ЛОКАЛЬНО в `~/lang`. НИКОГДА через SSH на VM. -- Разрешены ТОЛЬКО две операции: `git commit` и `git push`. -- ЗАПРЕЩЕНО: git pull, git fetch, git rebase, git merge, git reset, git stash, git checkout — что угодно кроме commit и push. -- Если push отклонён — СТОП, доложить пользователю. Не лезть в pull/merge/rebase самостоятельно. - -Версионирование тегами: `vMAJOR.MINOR.PATCH` -- Patch — любое изменение кода -- Minor — новая фича / компонент -- Major — breaking change - -```bash -git tag vX.Y.Z && git push origin vX.Y.Z +cd ~/lang && bash deploy_lang.sh ``` +build.sh → dist/index.html → scp на германскую ВМ. -## ⛔ ТЕРМИНАЛЬНЫЙ БУФЕР — НИКОГДА НЕ ЧИТАТЬ СТАРЫЙ +## Германская ВМ (Vultr) +- IP: 95.179.252.111 +- SSH: ssh -i ~/.ssh/vultr_openssh root@95.179.252.111 +- Статика: /var/www/lyngvo/index.html +- Nginx: /etc/nginx/conf.d/lang.kube5s.ru.conf +- Groq прокси: gunicorn 127.0.0.1:8765 (systemctl: groq-proxy) +- Домен: lang.kube5s.ru (Let's Encrypt) -**АБСОЛЮТНОЕ ПРАВИЛО:** -- get_terminal_output из старых сессий — МУСОР. Там старые прогоны. -- Всегда запускать новую команду через SSH и читать её вывод напрямую. -- НИКОГДА не читать буфер терминала из предыдущей сессии как актуальные данные. -- Актуальный результат — только из команды, которая была запущена СЕЙЧАС. - -## Документация тест-прогонов — В РЕАЛЬНОМ ВРЕМЕНИ - -**Правила:** -1. Перед запуском `run_all.sh` — создать файл `test-results/YYYY-MM-DD_HH-MM.log` и записать в него метку времени и что запускается. -2. Запускать `run_all.sh 2>&1 | tee ~/terra/lang/test-results/YYYY-MM-DD_HH-MM.log` — вывод пишется сразу в файл и отображается в терминале. -3. После завершения — rsync лога локально. Лог остаётся как документация. -4. Папка `test-results/` в репозитории — `.gitignore` не добавлять, логи коммитить. - -**Формат запуска:** -```bash -LOG="test-results/$(date +%Y-%m-%d_%H-%M).log" -ssh -i ~/.ssh/naeel_vm_id_ed25519 -o StrictHostKeyChecking=no naeel@5.172.178.213 \ - "bash ~/terra/lang/scripts/run_all.sh 2>&1 | tee ~/terra/lang/${LOG}" -rsync -az -e "ssh -i ~/.ssh/naeel_vm_id_ed25519 -o StrictHostKeyChecking=no" \ - naeel@5.172.178.213:~/terra/lang/test-results/ ~/lang/test-results/ -``` - -**Никогда не разбираться с результатами по памяти / буферу / чату. Только лог.** - -## ⛔ РУЧНЫЕ ПАТЧИ — ЗАПРЕЩЕНЫ - -- НИКОГДА не применять ручные патчи (`kubectl patch`, `kubectl apply` отдельных полей, `python -c` замены в yaml и т.д.) без явного указания. -- Все изменения — только через код (Helm chart, YAML, Go-код) + сборка + деплой. -- Ручной патч слетает при следующем helm upgrade/redeploy → регрессия. -- Исключение: только если пользователь явно написал "примени ручной патч". - -## Поведение агента - -- Не трогать рабочий код без явного указания -- Не делать НИЧЕГО сверх того, о чём явно приказали — ни git-команд, ни rebase, ни дополнительных шагов -- Если для продолжения нужен выбор — СПРОСИТЬ разрешения, не делать самостоятельно -- Деструктивные операции (`kubectl delete`, `rm -rf`, `terraform destroy` и др.) — только после явного подтверждения с указанием конкретных объектов -- Отвечать кратко, без вступлений, извинений, благодарностей и прочей воды +## Структура +lang/ +├── index.html каркас +├── css/style.css +├── js/ storage | syllables | transcribe | audio | compare | main +├── build.sh склейка → dist/ +├── dist/index.html собранный (деплоится) +├── deploy_lang.sh build + scp +└── doc/ diff --git a/k8s/deployment.yaml b/k8s/deployment.yaml deleted file mode 100644 index 27ae6d3..0000000 --- a/k8s/deployment.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: lyngvo - namespace: default -spec: - replicas: 1 - selector: - matchLabels: - app: lyngvo - template: - metadata: - labels: - app: lyngvo - spec: - containers: - - name: lyngvo - image: nginx:alpine - ports: - - containerPort: 80 - volumeMounts: - - name: html - mountPath: /usr/share/nginx/html - - name: nginx-conf - mountPath: /etc/nginx/conf.d/default.conf - subPath: default.conf - volumes: - - name: html - configMap: - name: lyngvo-html - - name: nginx-conf - configMap: - name: lyngvo-nginx ---- -apiVersion: v1 -kind: Service -metadata: - name: lyngvo - namespace: default -spec: - selector: - app: lyngvo - ports: - - port: 80 - targetPort: 80 diff --git a/k8s/ingress.yaml b/k8s/ingress.yaml deleted file mode 100644 index 6a51557..0000000 --- a/k8s/ingress.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: lyngvo - namespace: default - annotations: - nginx.ingress.kubernetes.io/ssl-redirect: "true" - cert-manager.io/cluster-issuer: letsencrypt-prod - nginx.ingress.kubernetes.io/hsts: "true" - nginx.ingress.kubernetes.io/hsts-max-age: "31536000" - nginx.ingress.kubernetes.io/hsts-include-subdomains: "true" -spec: - ingressClassName: nginx - tls: - - hosts: - - capire.kube5s.ru - secretName: capire-tls - rules: - - host: capire.kube5s.ru - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: lyngvo - port: - number: 80 diff --git a/nginx.conf b/nginx.conf deleted file mode 100644 index 7f103f5..0000000 --- a/nginx.conf +++ /dev/null @@ -1,17 +0,0 @@ -server { - listen 80; - server_name lang.kube5s.ru; - root /usr/share/nginx/html; - index index.html; - - # Агрессивный запрет кеширования — Chrome HSTS/cache bypass - add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always; - add_header Pragma "no-cache" always; - add_header Expires "0" always; - add_header X-Content-Type-Options "nosniff" always; - add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; - - location / { - try_files $uri $uri/ /index.html; - } -} diff --git a/ws_server.py b/ws_server.py deleted file mode 100644 index 8c730b6..0000000 --- a/ws_server.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env python3 -"""WebSocket Groq Whisper proxy — chunked text frames (DPI-proof).""" -import asyncio, json, websockets, requests, uuid, sys, time, base64 - -GROQ_BASE = "https://api.groq.com" - -def log(msg): - print(f"[{time.strftime('%H:%M:%S')}] {msg}", file=sys.stderr, flush=True) - -async def handler(ws): - chunks = {} # index -> base64_part - total = None - token = "" - mime = "audio/webm" - try: - log(f"conn from {ws.remote_address}") - async for raw in ws: - msg = json.loads(raw) - ci = msg.get("c", 0) - - if ci == -1: - # Final chunk — reassemble - if not chunks: - await ws.send(json.dumps({"error": "no chunks"})) - return - audio_b64 = "".join(chunks[i] for i in sorted(chunks)) - log(f"assembled {len(chunks)} chunks → {len(audio_b64)} chars, decoding...") - audio_data = base64.b64decode(audio_b64) - log(f"decoded {len(audio_data)}B") - - ext = "webm" - if "ogg" in mime: ext = "ogg" - elif "mp4" in mime or "aac" in mime: ext = "mp4" - - boundary = "----WKFBoundary" + uuid.uuid4().hex[:16] - payload = b"".join([ - f'--{boundary}\r\nContent-Disposition: form-data; name="file"; filename="audio.{ext}"\r\nContent-Type: {mime}\r\n\r\n'.encode(), - audio_data, - f'\r\n--{boundary}\r\nContent-Disposition: form-data; name="model"\r\n\r\nwhisper-large-v3'.encode(), - f'\r\n--{boundary}\r\nContent-Disposition: form-data; name="language"\r\n\r\nit'.encode(), - f'\r\n--{boundary}--\r\n'.encode(), - ]) - - t0 = time.time() - loop = asyncio.get_event_loop() - r = await loop.run_in_executor(None, lambda: requests.post( - f"{GROQ_BASE}/openai/v1/audio/transcriptions", - headers={ - "Authorization": f"Bearer {token}", - "Content-Type": f"multipart/form-data; boundary={boundary}", - }, - data=payload, timeout=60 - )) - log(f"Groq {r.status_code} in {time.time()-t0:.2f}s: {r.text[:120]}") - await ws.send(json.dumps({"text": r.json().get("text", ""), "status": r.status_code})) - return - else: - # Data chunk - chunks[ci] = msg.get("b", "") - token = msg.get("t", token) - mime = msg.get("m", mime) - total = msg.get("n", total) - # log(f"chunk {ci+1}/{total}") - - except Exception as e: - log(f"ERR: {e}") - try: await ws.send(json.dumps({"error": str(e)})) - except: pass - -async def main(): - async with websockets.serve(handler, "127.0.0.1", 8766): - log("groq-ws :8766 (chunked)") - await asyncio.Future() - -asyncio.run(main())