germany: удалён кубер-наследие, обновлены .github под германскую ВМ
This commit is contained in:
@@ -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. Только ответить на вопрос
|
- Статика: HTML/CSS/JS (без фреймворков)
|
||||||
2. ОСТАНОВИТЬСЯ
|
- Сборка: build.sh склеивает CSS/JS в dist/index.html
|
||||||
3. ЖДАТЬ следующей команды
|
- Деплой: scp dist/index.html → /var/www/lyngvo/
|
||||||
**ЗАПРЕЩЕНО** начинать работу, писать код, запускать команды — без явного "делай".
|
- API: Groq Whisper + Llama через /openai/ прокси
|
||||||
|
|
||||||
## ⛔⛔⛔ НЕ ТРОГАТЬ РАБОЧИЙ КОД — АБСОЛЮТНЫЙ ЗАПРЕТ НАВСЕГДА
|
|
||||||
|
|
||||||
**НИКАКИХ самодеятельных изменений рабочего кода:**
|
|
||||||
- Никаких "оптимизаций", "улучшений", "рефакторинга" без команды
|
|
||||||
- Никаких новых фич без явного разрешения
|
|
||||||
- Никаких 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
|
|
||||||
- СТОП — жду команды дальше
|
|
||||||
- **ЗАПРЕЩЕНО:** обновлять манифесты, заливать образы, применять на кластер, запускать тесты
|
|
||||||
|
|
||||||
**Исключение:** только если приказ явно включает цепочку ("собери И залей И тесты")
|
|
||||||
|
|||||||
+28
-102
@@ -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 'КОМАНДА'
|
|
||||||
```
|
```
|
||||||
|
cd ~/lang && bash deploy_lang.sh
|
||||||
Запрещено локально: `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
|
|
||||||
```
|
```
|
||||||
|
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 из старых сессий — МУСОР. Там старые прогоны.
|
lang/
|
||||||
- Всегда запускать новую команду через SSH и читать её вывод напрямую.
|
├── index.html каркас
|
||||||
- НИКОГДА не читать буфер терминала из предыдущей сессии как актуальные данные.
|
├── css/style.css
|
||||||
- Актуальный результат — только из команды, которая была запущена СЕЙЧАС.
|
├── js/ storage | syllables | transcribe | audio | compare | main
|
||||||
|
├── build.sh склейка → dist/
|
||||||
## Документация тест-прогонов — В РЕАЛЬНОМ ВРЕМЕНИ
|
├── dist/index.html собранный (деплоится)
|
||||||
|
├── deploy_lang.sh build + scp
|
||||||
**Правила:**
|
└── doc/
|
||||||
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` и др.) — только после явного подтверждения с указанием конкретных объектов
|
|
||||||
- Отвечать кратко, без вступлений, извинений, благодарностей и прочей воды
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
-17
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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())
|
|
||||||
Reference in New Issue
Block a user