feat: initial commit - Jhonny Editor
- Adicionado estrutura completa do projeto - Configurado MCP server para Premiere Pro - Adicionado documentação e skills - Configurado Gitignore para o projeto
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
# DEV-NOTES — Registro de implementações (Ronald)
|
||||
|
||||
> Este arquivo é a **fonte da mensagem de commit**.
|
||||
> O script `admin/update.command` lê automaticamente a seção `## NOVO NESTE LOTE`
|
||||
> abaixo (se houver texto) e usa o conteúdo no corpo da mensagem no momento do commit.
|
||||
>
|
||||
> **Como usar:**
|
||||
> 1. Ao trabalhar, adicione bullets em `## NOVO NESTE LOTE` descrevendo o que mudou.
|
||||
> 2. Depois rode `admin/update.command` — ele monta o commit com esse texto + resumo
|
||||
> automático dos arquivos alterados, faz build/deploy, commita e dá push.
|
||||
> 3. Após o script rodar com sucesso, você pode mover os bullets desta seção para
|
||||
> `## Histórico` e esvaziar `NOVO NESTE LOTE`.
|
||||
|
||||
---
|
||||
## NOVO NESTE LOTE
|
||||
<!-- Cole aqui as implementações do lote atual (uma por linha, resumida). Exemplo:
|
||||
- adicionada rota /captions/ai com detecção por lotes
|
||||
- corrigido bug na transcrição de áudio
|
||||
- atualizada tradução pt-BR dos painéis (2909 chaves)
|
||||
-->
|
||||
- adicionado admin/update.command (automatizacao de build+deploy+commit+push)
|
||||
- adicionado admin/DEV-NOTES.md (fonte de texto para a mensagem de commit)
|
||||
|
||||
## Uso / lembretes (não apagar)
|
||||
- `admin/update.command` → build + deploy + commit + push em um só
|
||||
- `admin/DEV-NOTES.md` → fonte da mensagem (seção NOVO NESTE LOTE)
|
||||
|
||||
## Histórico
|
||||
- Versão inicial: inicialização do repositório Ronald (backend OpenCut + painéis UXP/CEP + admin)
|
||||
|
||||
## Anotações / pendências
|
||||
- Os PNGs de `code/extension/design/` (~46 MB) são mockups de system design — avaliar se mantém ou otimiza.
|
||||
- `admin/VPS-ACCESS.md` é o único arquivo com credenciais e NÃO deve ser commitado (já está no .gitignore).
|
||||
Executable
+81
@@ -0,0 +1,81 @@
|
||||
#!/bin/zsh
|
||||
# ============================================================
|
||||
# OpenCut Ronald - admin/OpenCut.command
|
||||
#
|
||||
# PONTO DE ENTRADA UNICO. Rode este arquivo a cada atualizacao que
|
||||
# desenvolvermos: ele executa os tres scripts da pasta admin em ordem.
|
||||
#
|
||||
# 1. update.command dependencias + build dos paineis + testes
|
||||
# 2. deploy.command backend no ar + ponte de live-updates
|
||||
# 3. commit.command commit + push (pergunta antes de publicar)
|
||||
#
|
||||
# Se o update encontrar pendencias (teste quebrado, build falhando),
|
||||
# ele PARA antes do commit - nada de publicar arvore quebrada. Para
|
||||
# seguir mesmo assim, use FORCE=1.
|
||||
#
|
||||
# Uso:
|
||||
# admin/OpenCut.command ciclo completo
|
||||
# AUTO=1 admin/OpenCut.command nao pergunta antes do push
|
||||
# SKIP_TESTS=1 admin/OpenCut.command pula os testes do passo 1
|
||||
# FORCE=1 admin/OpenCut.command commita mesmo com pendencias
|
||||
# ONLY=deploy admin/OpenCut.command roda so uma etapa
|
||||
# (update | deploy | commit)
|
||||
# ============================================================
|
||||
set -u
|
||||
|
||||
ADMIN="$(cd "$(dirname "$0")" && pwd -P)"
|
||||
|
||||
GRN="$(tput setaf 2 2>/dev/null)"; YLW="$(tput setaf 3 2>/dev/null)"
|
||||
RD="$(tput setaf 1 2>/dev/null)"; BLU="$(tput setaf 4 2>/dev/null)"
|
||||
NC="$(tput sgr0 2>/dev/null)"
|
||||
step() { echo; echo "${BLU}============================================================${NC}"
|
||||
echo "${BLU} $*${NC}"
|
||||
echo "${BLU}============================================================${NC}"; }
|
||||
ok() { echo "${GRN}-> OK ${NC}$*"; }
|
||||
warn() { echo "${YLW}-> Aviso: ${NC}$*"; }
|
||||
die() { echo "${RD}-> Erro: ${NC}$*" >&2; exit 1; }
|
||||
|
||||
ONLY="${ONLY:-all}"
|
||||
run_step() { [ "$ONLY" = "all" ] || [ "$ONLY" = "$1" ]; }
|
||||
|
||||
for s in update deploy commit; do
|
||||
[ -x "$ADMIN/$s.command" ] || die "$ADMIN/$s.command ausente ou sem permissao de execucao"
|
||||
done
|
||||
|
||||
UPDATE_RC=0
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 1) Atualizacoes
|
||||
# ------------------------------------------------------------
|
||||
if run_step update; then
|
||||
step "1/3 ATUALIZACOES (dependencias, build, testes)"
|
||||
"$ADMIN/update.command" || UPDATE_RC=$?
|
||||
if [ "$UPDATE_RC" != "0" ]; then
|
||||
warn "o update terminou com pendencias (codigo $UPDATE_RC)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 2) Deploy
|
||||
# ------------------------------------------------------------
|
||||
if run_step deploy; then
|
||||
step "2/3 DEPLOY (backend + ponte de live-updates)"
|
||||
"$ADMIN/deploy.command" || warn "o deploy nao confirmou o backend no ar"
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 3) Commit
|
||||
# ------------------------------------------------------------
|
||||
if run_step commit; then
|
||||
if [ "$UPDATE_RC" != "0" ] && [ "${FORCE:-0}" != "1" ]; then
|
||||
step "3/3 COMMIT - PULADO"
|
||||
warn "o passo 1 acusou pendencias; nao vou commitar uma arvore quebrada."
|
||||
warn "corrija os avisos acima, ou rode de novo com FORCE=1 para commitar assim mesmo."
|
||||
exit "$UPDATE_RC"
|
||||
fi
|
||||
step "3/3 COMMIT (commit + push)"
|
||||
"$ADMIN/commit.command" || die "o commit falhou"
|
||||
fi
|
||||
|
||||
echo
|
||||
ok "Ciclo concluido."
|
||||
Executable
+217
@@ -0,0 +1,217 @@
|
||||
#!/bin/zsh
|
||||
# ============================================================
|
||||
# Jhonny - admin/PremiereMCP.command
|
||||
#
|
||||
# PONTO DE ENTRADA UNICO do MCP do Premiere Pro (code/) e do pipeline
|
||||
# de corte de silencio por transcricao (Whisper). Rode este arquivo
|
||||
# sempre que mexer no codigo antes de testar dentro do Premiere.
|
||||
#
|
||||
# 1. BUILD compila o TypeScript (code/, tsc -> dist/), valida a
|
||||
# sintaxe dos scripts do painel CEP e do apply-silence-cuts,
|
||||
# e reinstala a COPIA do painel que o Premiere realmente
|
||||
# carrega (~/Library/.../Adobe/CEP/extensions/MCPBridgeCEP -
|
||||
# editar code/cep-plugin sozinho nao e o bastante)
|
||||
# 2. PYTHON confere o venv/dependencias do pipeline de transcricao
|
||||
# (faster-whisper) e a presenca dos scripts esperados
|
||||
# 3. RAG reindexa na RAG do Jhonny (jhonny-rag) o que mudou em
|
||||
# code/ desde o ultimo build - mantem o indice em dia pro
|
||||
# agente de IA buscar o codigo certo. Best-effort: se o
|
||||
# abre o tunel temporariamente quando necessario e fecha o
|
||||
# processo que este ciclo iniciou ao terminar.
|
||||
# 4. LEMBRETE reinicie o Premiere Pro por completo para o painel
|
||||
# reinstalado ser carregado - este passo so imprime isso
|
||||
#
|
||||
# Nao mexe em git (este projeto nao e um repositorio git) e nao sobe
|
||||
# nenhum servidor - o bridge roda dentro do proprio Premiere via CEP.
|
||||
#
|
||||
# Uso:
|
||||
# admin/PremiereMCP.command ciclo completo
|
||||
# SKIP_BUILD=1 admin/PremiereMCP.command pula o build TypeScript
|
||||
# SKIP_PYTHON=1 admin/PremiereMCP.command pula a checagem Python
|
||||
# SKIP_RAG=1 admin/PremiereMCP.command pula a reindexacao RAG
|
||||
# ONLY=build admin/PremiereMCP.command roda so uma etapa
|
||||
# (build | python | rag | reload)
|
||||
# ============================================================
|
||||
set -u
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||
CODE="$ROOT/code"
|
||||
CEP="$CODE/cep-plugin"
|
||||
SCRIPTS="$CODE/scripts"
|
||||
|
||||
# Pipeline de transcricao (Whisper) - mesmo venv usado nesta sessao.
|
||||
TRANSCRICAO="/Volumes/Merongo/SISTEMAS/WHISPERX/transcricao"
|
||||
VENV_PY="/Volumes/Merongo/SISTEMAS/venvs/whisperx-transcricao/bin/python"
|
||||
|
||||
GRN="$(tput setaf 2 2>/dev/null)"; YLW="$(tput setaf 3 2>/dev/null)"
|
||||
RD="$(tput setaf 1 2>/dev/null)"; BLU="$(tput setaf 4 2>/dev/null)"
|
||||
NC="$(tput sgr0 2>/dev/null)"
|
||||
step() { echo; echo "${BLU}============================================================${NC}"
|
||||
echo "${BLU} $*${NC}"
|
||||
echo "${BLU}============================================================${NC}"; }
|
||||
ok() { echo "${GRN}-> OK ${NC}$*"; }
|
||||
warn() { echo "${YLW}-> Aviso: ${NC}$*"; }
|
||||
die() { echo "${RD}-> Erro: ${NC}$*" >&2; exit 1; }
|
||||
|
||||
ONLY="${ONLY:-all}"
|
||||
run_step() { [ "$ONLY" = "all" ] || [ "$ONLY" = "$1" ]; }
|
||||
|
||||
FAILED=0
|
||||
RAG_TUNNEL_PID=""
|
||||
|
||||
cleanup_rag_tunnel() {
|
||||
if [ -n "$RAG_TUNNEL_PID" ] && kill -0 "$RAG_TUNNEL_PID" 2>/dev/null; then
|
||||
kill "$RAG_TUNNEL_PID" 2>/dev/null || true
|
||||
wait "$RAG_TUNNEL_PID" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
trap cleanup_rag_tunnel EXIT
|
||||
|
||||
[ -d "$CODE" ] || die "pasta do projeto nao encontrada: $CODE"
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 1) Build (TypeScript + validacao de sintaxe dos scripts)
|
||||
# ------------------------------------------------------------
|
||||
if run_step build && [ "${SKIP_BUILD:-0}" != "1" ]; then
|
||||
step "1/4 BUILD (tsc + validacao de sintaxe)"
|
||||
cd "$CODE" || die "nao consegui entrar em $CODE"
|
||||
|
||||
if npm run build; then
|
||||
ok "npm run build (dist/ atualizado)"
|
||||
else
|
||||
warn "npm run build falhou - veja o log acima"
|
||||
FAILED=1
|
||||
fi
|
||||
|
||||
for f in "$CEP/main.js" "$SCRIPTS/apply-silence-cuts.mjs"; do
|
||||
if [ -f "$f" ]; then
|
||||
if node --check "$f" 2>/dev/null; then
|
||||
ok "sintaxe valida: ${f#$ROOT/}"
|
||||
else
|
||||
warn "sintaxe invalida: ${f#$ROOT/}"
|
||||
FAILED=1
|
||||
fi
|
||||
else
|
||||
warn "arquivo esperado nao encontrado: ${f#$ROOT/}"
|
||||
FAILED=1
|
||||
fi
|
||||
done
|
||||
|
||||
# O Premiere carrega o painel de uma COPIA instalada em
|
||||
# ~/Library/.../Adobe/CEP/extensions/MCPBridgeCEP, nao direto de
|
||||
# code/cep-plugin. Sem este passo, mudancas no painel nunca aparecem
|
||||
# dentro do Premiere, mesmo com o app reaberto.
|
||||
if node dist/index.js --install-cep >/tmp/premiere-mcp-install-cep.log 2>&1; then
|
||||
ok "painel CEP reinstalado (copia sincronizada)"
|
||||
else
|
||||
warn "falha ao reinstalar o painel CEP - veja /tmp/premiere-mcp-install-cep.log"
|
||||
FAILED=1
|
||||
fi
|
||||
elif run_step build; then
|
||||
step "1/4 BUILD - PULADO (SKIP_BUILD=1)"
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 2) Pipeline Python (Whisper / corte de silencio)
|
||||
# ------------------------------------------------------------
|
||||
if run_step python && [ "${SKIP_PYTHON:-0}" != "1" ]; then
|
||||
step "2/4 PYTHON (venv + pipeline de transcricao)"
|
||||
|
||||
if [ -x "$VENV_PY" ]; then
|
||||
ok "venv encontrado: $VENV_PY"
|
||||
else
|
||||
warn "venv nao encontrado em $VENV_PY"
|
||||
FAILED=1
|
||||
fi
|
||||
|
||||
if [ -x "$VENV_PY" ] && "$VENV_PY" -c "import faster_whisper" 2>/dev/null; then
|
||||
ok "faster-whisper importavel"
|
||||
else
|
||||
warn "faster-whisper nao importavel neste venv"
|
||||
FAILED=1
|
||||
fi
|
||||
|
||||
for f in "$TRANSCRICAO/transcribe_cli.py" "$TRANSCRICAO/silence_cutter.py"; do
|
||||
if [ -f "$f" ]; then
|
||||
ok "script presente: ${f#/Volumes/Merongo/}"
|
||||
else
|
||||
warn "script esperado nao encontrado: ${f#/Volumes/Merongo/}"
|
||||
FAILED=1
|
||||
fi
|
||||
done
|
||||
elif run_step python; then
|
||||
step "2/4 PYTHON - PULADO (SKIP_PYTHON=1)"
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 3) RAG - reindexa na RAG do Jhonny o que mudou em code/
|
||||
# ------------------------------------------------------------
|
||||
if run_step rag && [ "${SKIP_RAG:-0}" != "1" ]; then
|
||||
step "3/4 RAG (reindexacao incremental jhonny-rag)"
|
||||
RAG_PY="$ROOT/rag/.venv/bin/python3"
|
||||
[ -x "$RAG_PY" ] || RAG_PY="$(command -v python3)"
|
||||
if ! nc -z 127.0.0.1 55435 2>/dev/null; then
|
||||
echo "-> Abrindo tunel RAG temporario (127.0.0.1:55435)..."
|
||||
ssh -N \
|
||||
-o ExitOnForwardFailure=yes \
|
||||
-o ServerAliveInterval=30 \
|
||||
-o ServerAliveCountMax=3 \
|
||||
-L "127.0.0.1:55435:127.0.0.1:55435" \
|
||||
root@179.197.228.240 &
|
||||
RAG_TUNNEL_PID=$!
|
||||
|
||||
RAG_TUNNEL_READY=0
|
||||
for _ in $(seq 1 10); do
|
||||
if nc -z 127.0.0.1 55435 2>/dev/null; then
|
||||
RAG_TUNNEL_READY=1
|
||||
break
|
||||
fi
|
||||
if ! kill -0 "$RAG_TUNNEL_PID" 2>/dev/null; then
|
||||
break
|
||||
fi
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
if [ "$RAG_TUNNEL_READY" != "1" ]; then
|
||||
warn "nao foi possivel abrir o tunel RAG - reindexacao pulada."
|
||||
else
|
||||
ok "tunel RAG aberto temporariamente"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$RAG_TUNNEL_PID" ] && ! nc -z 127.0.0.1 55435 2>/dev/null; then
|
||||
warn "tunel RAG ficou indisponivel - reindexacao pulada."
|
||||
elif ! nc -z 127.0.0.1 55435 2>/dev/null; then
|
||||
warn "tunel RAG fechado - reindexacao pulada."
|
||||
elif [ ! -x "$RAG_PY" ]; then
|
||||
warn "python do rag nao encontrado - reindexacao pulada."
|
||||
else
|
||||
if ( cd "$ROOT" && RAG_ENV_FILE="$ROOT/rag/jhonny.env" \
|
||||
"$RAG_PY" "$ROOT/rag/index_code.py" >/tmp/premiere-mcp-rag.log 2>&1 ); then
|
||||
ok "RAG reindexado (incremental) - índice em dia"
|
||||
else
|
||||
warn "reindexacao RAG falhou - veja /tmp/premiere-mcp-rag.log"
|
||||
FAILED=1
|
||||
fi
|
||||
fi
|
||||
elif run_step rag; then
|
||||
step "3/4 RAG - PULADO (SKIP_RAG=1)"
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 4) Lembrete de recarregar o painel dentro do Premiere
|
||||
# ------------------------------------------------------------
|
||||
if run_step reload; then
|
||||
step "4/4 REINICIAR O PREMIERE"
|
||||
echo "O painel ja foi reinstalado (passo 1), mas o Premiere so carrega a"
|
||||
echo "copia nova depois de um restart completo:"
|
||||
echo " 1. Feche o Premiere Pro por completo (Cmd+Q)"
|
||||
echo " 2. Abra de novo e va em Window > Extensions > MCP for Adobe Premiere Pro"
|
||||
echo " 3. Confira se aparecem as abas 'Bridge' e 'Cortar silencio'"
|
||||
fi
|
||||
|
||||
echo
|
||||
if [ "$FAILED" != "0" ]; then
|
||||
die "ciclo terminou com pendencias - corrija os avisos acima antes de testar no Premiere."
|
||||
fi
|
||||
ok "Ciclo concluido. Pronto para recarregar o painel no Premiere."
|
||||
@@ -0,0 +1,126 @@
|
||||
# Acesso à VPS — Doza
|
||||
|
||||
> Mesma VPS usada pelo projeto CRM (`/Volumes/Merongo/SISTEMAS/GENIAL SISTEMAS/CRM/admin/VPS-ACCESS.md`).
|
||||
> Copiado de lá em 2026-08-31 para uso no projeto Doza.
|
||||
|
||||
> ⚠️ **Nunca coloque a senha em texto puro em chat, commit ou qualquer lugar além deste arquivo.**
|
||||
> A autenticação por chave SSH já está configurada — não é preciso senha no uso normal.
|
||||
> Este arquivo não deve ser commitado (adicione a um `.gitignore` se o projeto virar repositório git).
|
||||
|
||||
## Credenciais
|
||||
|
||||
```
|
||||
VPS_HOST=179.197.228.240
|
||||
VPS_USER=root
|
||||
SSH_KEY=~/.ssh/id_ed25519
|
||||
```
|
||||
|
||||
> Senha (`VPS_PASSWORD`) existe só como fallback de emergência — está guardada
|
||||
> apenas no arquivo original do projeto CRM, não duplicada aqui. Autenticação
|
||||
> normal é sempre por chave SSH.
|
||||
|
||||
Domínio base (wildcard automático da Hostinger, HTTPS via Let's Encrypt/Traefik):
|
||||
|
||||
```
|
||||
TRAEFIK_HOST=srv1838065.hstgr.cloud
|
||||
```
|
||||
|
||||
## Como conectar (sem senha)
|
||||
|
||||
A chave pública `~/.ssh/id_ed25519.pub` já está instalada em
|
||||
`/root/.ssh/authorized_keys` da VPS.
|
||||
|
||||
```bash
|
||||
# conectar e executar um comando
|
||||
ssh root@179.197.228.240 "<comando>"
|
||||
|
||||
# conectar interativo
|
||||
ssh root@179.197.228.240
|
||||
|
||||
# copiar arquivos (rsync)
|
||||
rsync -az -e "ssh" ./ root@179.197.228.240:/caminho/no/servidor/
|
||||
```
|
||||
|
||||
Alias opcional (se configurado em `~/.ssh/config`):
|
||||
|
||||
```bash
|
||||
ssh gestor-trafego-vps "<comando>"
|
||||
```
|
||||
|
||||
## O que já está rodando na VPS (relevante para o Doza)
|
||||
|
||||
Levantado via `ssh root@179.197.228.240 "docker ps"` em 2026-08-31. Cada app
|
||||
fica isolado em `/docker/<nome-do-projeto>/` com seu próprio `docker-compose.yml`.
|
||||
|
||||
- `traefik` (network_mode host, Let's Encrypt automático) — **não mexer**.
|
||||
- `openclaw` (agente da Hostinger) — **não mexer**.
|
||||
- Stack **Supabase completa** já rodando: `supabase-db` (Postgres 17),
|
||||
`supabase-pooler` (porta 5432/6543 públicas), `supabase-rest`
|
||||
(PostgREST v14.12), `supabase-kong` (porta 8000/8443), `supabase-studio`,
|
||||
`supabase-auth`, `supabase-storage`, `supabase-meta`, `supabase-realtime`,
|
||||
`supabase-imgproxy`, `supabase-edge-functions`. **Já é Postgres + PostgREST
|
||||
prontos** — antes de subir um stack RAG novo, avaliar se dá para usar
|
||||
schemas isolados aqui em vez de duplicar infraestrutura.
|
||||
- `genial-crm-postgres-1` já usa imagem `pgvector/pgvector:pg16` (porta local
|
||||
`127.0.0.1:57432`) — pertence ao projeto CRM, não usar para o Doza.
|
||||
- `ollama` (porta 11434) e `open-webui` — modelos locais já disponíveis na VPS.
|
||||
- `automacoes-db` (container Docker avulso, porta local `127.0.0.1:5433`) —
|
||||
criado via "Gerenciador Docker" do painel Hostinger, é o alvo original
|
||||
para um novo container RAG (pgvector + PostgREST) discutido nesta sessão.
|
||||
- `n8n` (porta 5678), `gitea`, `evolution-go`/`evolution-api`,
|
||||
`gestor-trafego` (porta 4001), `wacrm20` (porta 3000).
|
||||
|
||||
## RAG Hub — banco compartilhado de desenvolvimento (equipe, não só Doza)
|
||||
|
||||
Criado em 2026-08-31 (renomeado de `rag-hub-doza` para `rag-hub` no mesmo dia,
|
||||
assim que decidido que a infra é compartilhada). Stack própria, sem relação
|
||||
com Supabase/CRM/genial-crm — serve exclusivamente para a equipe de dev usar
|
||||
como RAG (indexação de código/docs) e reduzir tokens gastos por IAs ao
|
||||
consultar os projetos. **Não é o banco de produção de nenhum sistema.**
|
||||
|
||||
Modelo: **um container Postgres+pgvector só**, e **um banco por sistema**
|
||||
dentro dele (CRM, Doza, contabilidade, etc. cada um cria o seu). Mais barato
|
||||
de manter (1 backup, 1 upgrade) do que um container Docker por projeto.
|
||||
|
||||
- Local na VPS: `/docker/rag-hub/` (compose + `.env`, `chmod 600`, gerado
|
||||
isolado nesta instalação).
|
||||
- Container: `rag-hub-db` (imagem `pgvector/pgvector:pg16`).
|
||||
- **Não exposto publicamente** — porta ligada só em `127.0.0.1:55435` na VPS.
|
||||
Acesso externo (ex: do Mac/Mini M4) é via túnel SSH:
|
||||
|
||||
```bash
|
||||
ssh -N -L 55435:127.0.0.1:55435 root@179.197.228.240
|
||||
# depois conectar em localhost:55435 normalmente
|
||||
```
|
||||
|
||||
- Usuário admin: `rag_admin`, senha em `/docker/rag-hub/.env` na própria VPS
|
||||
(não duplicada aqui).
|
||||
- Extensões ativas: `vector` (pgvector 0.8.6), `pg_trgm` (habilitadas no
|
||||
banco `rag_doza`; ao criar um banco novo pra outro sistema, rodar
|
||||
`CREATE EXTENSION vector; CREATE EXTENSION pg_trgm;` nele também).
|
||||
- **Banco do Doza**: `rag_doza`, schema `doza`, tabela inicial
|
||||
`doza.code_chunks` (id, file_path, content, chunk_index,
|
||||
embedding vector(768), updated_at) com índice ivfflat (cosine, lists=100).
|
||||
- **Banco do Jhonny** (projeto MCP TypeScript em `code/`): `jhonny-rag`,
|
||||
schema `jhonny-rag`. Criado em 2026-09-07. Usa o mesmo padrão do
|
||||
`rag_tigre` (busca híbrida HNSW + pg_trgm, `file_index` com
|
||||
`summary_embedding`). Role dedicada: `jhonny-rag` (grants por schema, sem
|
||||
DDL). Wrappers locais: `rag/index_jhonny.sh`, `rag/search_jhonny.sh`,
|
||||
`rag/migrate_jhonny.sh`; credenciais em `rag/jhonny.env`. Índice: 505
|
||||
arquivos de `code/` (TypeScript), 3479 chunks. Como o schema tem um guífen,
|
||||
o `search.py`/`index_code.py` escapam o identificador com aspas duplas
|
||||
(`_qid`); os bancos sem guífen (doza/tigre) seguem intactos e funcionando.
|
||||
- **Para adicionar outro sistema** (ex: CRM, contabilidade): conectar como
|
||||
`rag_admin` e rodar `CREATE DATABASE rag_crm OWNER rag_admin;` (ou outro
|
||||
nome), depois `\c rag_crm` e criar extensões/schema/tabelas próprias —
|
||||
sem mexer no banco `rag_doza`.
|
||||
- Init SQL do banco `rag_doza` versionado em `/docker/rag-hub/init/01-init.sql`
|
||||
na VPS (roda automaticamente só na primeira subida do volume).
|
||||
- Subir/parar: `ssh root@179.197.228.240 "cd /docker/rag-hub && docker compose up -d"` (ou `down`).
|
||||
|
||||
## Segurança
|
||||
|
||||
- A senha **não deve** aparecer em comandos, arquivos ou histórico do shell.
|
||||
- Se a chave SSH for comprometida, revogá-la removendo a linha de
|
||||
`/root/.ssh/authorized_keys` na VPS e gerar uma nova.
|
||||
- Manter `~/.ssh/id_ed25519` protegido (`chmod 600`).
|
||||
Executable
+127
@@ -0,0 +1,127 @@
|
||||
#!/bin/zsh
|
||||
# ============================================================
|
||||
# OpenCut Ronald - admin/commit.command
|
||||
#
|
||||
# COMMIT: monta a mensagem, commita e envia para o Gitea.
|
||||
# 1. Le a secao 'NOVO NESTE LOTE' do admin/DEV-NOTES.md
|
||||
# 2. Junta o resumo automatico dos arquivos alterados
|
||||
# 3. git add -A + commit + push
|
||||
#
|
||||
# O push e a unica etapa que sai desta maquina, entao ele pede
|
||||
# confirmacao. Use AUTO=1 para rodar sem perguntar (ex.: dentro do
|
||||
# OpenCut.command em modo automatico).
|
||||
#
|
||||
# Uso:
|
||||
# admin/commit.command commita e pergunta antes do push
|
||||
# DRY=1 admin/commit.command so mostra o plano, nao commita
|
||||
# AUTO=1 admin/commit.command commita e faz push sem perguntar
|
||||
# ============================================================
|
||||
set -u
|
||||
umask 077
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||
ADMIN="$ROOT/admin"
|
||||
NOTES="$ADMIN/DEV-NOTES.md"
|
||||
REMOTE="origin"
|
||||
|
||||
GRN="$(tput setaf 2 2>/dev/null)"; YLW="$(tput setaf 3 2>/dev/null)"
|
||||
RD="$(tput setaf 1 2>/dev/null)"; BLU="$(tput setaf 4 2>/dev/null)"
|
||||
NC="$(tput sgr0 2>/dev/null)"
|
||||
say() { echo "${BLU}== ${NC}$*"; }
|
||||
ok() { echo "${GRN}-> OK ${NC}$*"; }
|
||||
warn() { echo "${YLW}-> Aviso: ${NC}$*"; }
|
||||
die() { echo "${RD}-> Erro: ${NC}$*" >&2; exit 1; }
|
||||
|
||||
cd "$ROOT" || die "nao consegui entrar em $ROOT"
|
||||
[[ -d .git ]] || die "nao e um repositorio git em $ROOT"
|
||||
git remote get-url "$REMOTE" >/dev/null 2>&1 || die "remote '$REMOTE' nao configurado"
|
||||
|
||||
echo "${BLU}============================================================${NC}"
|
||||
say "commit.command - commit + push"
|
||||
say "repositorio: $(git remote get-url "$REMOTE")"
|
||||
say "branch: $(git branch --show-current)"
|
||||
echo "${BLU}============================================================${NC}"
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 1) Ha o que commitar?
|
||||
# ------------------------------------------------------------
|
||||
CHANGES="$(git status --porcelain)"
|
||||
if [[ -z "$CHANGES" ]]; then
|
||||
ok "Nenhuma alteracao para commitar."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
say "Alteracoes detectadas:"
|
||||
echo "$CHANGES" | sed 's/^/ /' | head -60
|
||||
echo " ... ($(echo "$CHANGES" | grep -c .) itens)"
|
||||
echo
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 2) Mensagem do commit
|
||||
# ------------------------------------------------------------
|
||||
MSG_NOTA=""
|
||||
if [[ -f "$NOTES" && -s "$NOTES" ]]; then
|
||||
MSG_NOTA="$(awk '
|
||||
/^## NOVO NESTE LOTE/ {f=1; next}
|
||||
/^## / && f {exit}
|
||||
f
|
||||
' "$NOTES" 2>/dev/null \
|
||||
| sed 's/^[[:space:]]*//; s/[[:space:]]*$//' \
|
||||
| grep -v '^$' \
|
||||
| awk '/^<!--/{c=1;next} /-->/&&c{c=0;next} c{next} {print}' \
|
||||
| sed 's/^[-*] //' \
|
||||
| grep -v '(_vazio' || true)"
|
||||
fi
|
||||
|
||||
FILES="$(git status --porcelain | sed 's/^.../ - /' | head -80)"
|
||||
STAT="$(git diff --stat HEAD | tail -1)"
|
||||
|
||||
if [[ -n "$MSG_NOTA" ]]; then
|
||||
BODY="$MSG_NOTA"
|
||||
else
|
||||
BODY="(sem nota em DEV-NOTES.md; veja os arquivos alterados abaixo)"
|
||||
fi
|
||||
|
||||
COMMIT_MSG="update automatizado: alteracoes no projeto
|
||||
$(printf '%s\n' "$BODY")
|
||||
|
||||
$STAT
|
||||
|
||||
Arquivos alterados:
|
||||
$FILES"
|
||||
|
||||
say "Mensagem do commit a ser usada:"
|
||||
echo "----------------------------------------------"
|
||||
echo "$COMMIT_MSG"
|
||||
echo "----------------------------------------------"
|
||||
echo
|
||||
|
||||
if [[ "${DRY:-0}" = "1" ]]; then
|
||||
say "DRY=1 - nao vou commitar/push."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 3) Commit
|
||||
# ------------------------------------------------------------
|
||||
say "Adicionando arquivos e commitando..."
|
||||
git add -A
|
||||
git commit -m "$COMMIT_MSG" || die "commit falhou"
|
||||
ok "commit criado: $(git rev-parse --short HEAD)"
|
||||
echo
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 4) Push (confirmado - e o passo que publica)
|
||||
# ------------------------------------------------------------
|
||||
if [[ "${AUTO:-0}" != "1" ]]; then
|
||||
printf "Enviar para '%s' agora? [s/N] " "$REMOTE"
|
||||
read -r RESP
|
||||
case "$RESP" in
|
||||
[sS]|[sS][iI][mM]|[yY]|[yY][eE][sS]) ;;
|
||||
*) warn "push cancelado - o commit ficou local (envie depois com: git push $REMOTE HEAD)"; exit 0 ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
say "Fazendo push para '$REMOTE'..."
|
||||
git push "$REMOTE" HEAD || die "push falhou"
|
||||
ok "push concluido"
|
||||
Executable
+128
@@ -0,0 +1,128 @@
|
||||
#!/bin/zsh
|
||||
# ============================================================
|
||||
# OpenCut Ronald - admin/deploy.command
|
||||
#
|
||||
# DEPLOY: garante o backend OpenCut no ar em http://127.0.0.1:5679
|
||||
# e sobe a ponte de live-updates (WebSocket) que o painel consome.
|
||||
#
|
||||
# E este o script que o painel do Premiere dispara quando pede
|
||||
# "iniciar backend" (via ~/.opencut/launcher.command). Por isso ele
|
||||
# nao pergunta nada e nao mexe no git: roda sozinho e sai.
|
||||
#
|
||||
# Uso:
|
||||
# admin/deploy.command sobe (ou recupera) o backend
|
||||
# OPENCUT_RESTART=1 admin/deploy.command reinicia o backend atual
|
||||
# OPENCUT_NO_OPEN=1 admin/deploy.command nao abre o navegador
|
||||
# ============================================================
|
||||
set -u
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||
CODE="$ROOT/code"
|
||||
START_SCRIPT="$CODE/start-opencut.sh"
|
||||
RUNTIME_DIR="$CODE/.opencut-runtime"
|
||||
LOG="$RUNTIME_DIR/server.log"
|
||||
URL="http://127.0.0.1:5679"
|
||||
HEALTH="$URL/health"
|
||||
RESTART="${OPENCUT_RESTART:-0}"
|
||||
|
||||
notify() { osascript -e "display notification \"$1\" with title \"OpenCut\"" 2>/dev/null || true; }
|
||||
open_ui() { [ "${OPENCUT_NO_OPEN:-0}" = "1" ] || open "$URL" 2>/dev/null || true; }
|
||||
|
||||
server_ok() { curl -s -m 5 "$HEALTH" 2>/dev/null | grep -q '"status":"ok"'; }
|
||||
|
||||
stop_opencut_pids() {
|
||||
local signal="$1"
|
||||
local pid
|
||||
for pid in ${(f)PGREP}; do
|
||||
[ -n "$pid" ] && kill "$signal" "$pid" 2>/dev/null || true
|
||||
done
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Ponte de live-updates (WebSocket)
|
||||
#
|
||||
# O backend nao sobe a ponte sozinho: ela so existe apos um POST em
|
||||
# /ws/start, e esse endpoint exige o token CSRF que o /health entrega.
|
||||
# Sem este passo o painel abre mostrando "Bridge stopped" e o usuario
|
||||
# precisa clicar em "Start" na mao a cada boot.
|
||||
# ------------------------------------------------------------
|
||||
start_bridge() {
|
||||
if curl -s -m 5 "$URL/ws/status" 2>/dev/null | grep -q '"running":true'; then
|
||||
echo "-> ponte de live-updates ja ativa"
|
||||
return 0
|
||||
fi
|
||||
local token
|
||||
token="$(curl -s -m 5 -H "Origin: $URL" "$HEALTH" 2>/dev/null \
|
||||
| /usr/bin/python3 -c 'import sys,json
|
||||
try: print(json.load(sys.stdin).get("csrf_token") or "")
|
||||
except Exception: print("")' 2>/dev/null)"
|
||||
if [ -z "$token" ]; then
|
||||
echo "-> Aviso: /health nao devolveu token CSRF; ponte nao iniciada"
|
||||
return 1
|
||||
fi
|
||||
if curl -s -m 10 -X POST "$URL/ws/start" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-OpenCut-Token: $token" \
|
||||
-H "Origin: $URL" -d '{}' 2>/dev/null | grep -q '"running":true'; then
|
||||
echo "-> ponte de live-updates ativa"
|
||||
return 0
|
||||
fi
|
||||
echo "-> Aviso: a ponte de live-updates nao subiu (veja $LOG)"
|
||||
return 1
|
||||
}
|
||||
|
||||
# 1) Ja esta no ar?
|
||||
if server_ok && [ "$RESTART" != "1" ]; then
|
||||
echo "-> backend ja rodando em $URL"
|
||||
start_bridge
|
||||
notify "Backend ja esta rodando em $URL"
|
||||
open_ui
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$RESTART" = "1" ]; then
|
||||
echo "-> reinicio solicitado; substituindo o backend atual"
|
||||
fi
|
||||
|
||||
# 2) Processo antigo travado? Derruba antes de subir outro.
|
||||
PGREP=$(pgrep -f 'opencut.server' || true)
|
||||
if [ -n "$PGREP" ]; then
|
||||
notify "Backend travado detectado - reiniciando..."
|
||||
echo "Encerrando processo(s) antigo(s): $PGREP"
|
||||
stop_opencut_pids TERM
|
||||
sleep 3
|
||||
PGREP=$(pgrep -f 'opencut.server' || true)
|
||||
[ -n "$PGREP" ] && stop_opencut_pids KILL
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
# 3) Sobe pelo start-opencut.sh (venv, modelos, ffmpeg e tmp no Merongo)
|
||||
if [ ! -x "$START_SCRIPT" ]; then
|
||||
echo "Erro: $START_SCRIPT nao encontrado ou sem permissao de execucao" >&2
|
||||
notify "Falha: start-opencut.sh ausente"
|
||||
exit 1
|
||||
fi
|
||||
mkdir -p "$RUNTIME_DIR"
|
||||
mkdir -p "/Volumes/Merongo/SISTEMAS/OPENCUT-MEDIA/tmp"
|
||||
nohup "$START_SCRIPT" > "$LOG" 2>&1 &
|
||||
NEW_PID=$!
|
||||
echo "Backend iniciado (PID $NEW_PID). Aguardando health check..."
|
||||
|
||||
# 4) Espera ate 60s pelo health check
|
||||
for i in {1..30}; do
|
||||
if server_ok; then
|
||||
start_bridge
|
||||
notify "Backend pronto em $URL"
|
||||
open_ui
|
||||
exit 0
|
||||
fi
|
||||
if ! kill -0 $NEW_PID 2>/dev/null; then
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
echo "Falha ao iniciar. Log em $LOG" >&2
|
||||
notify "Falha ao iniciar o backend. Veja $LOG"
|
||||
tail -30 "$LOG"
|
||||
exit 1
|
||||
Executable
+88
@@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Tigre (codeclass) - Deploy do servidor web novo em Swift
|
||||
# Sobe um nivel: o script vive em admin/, o projeto e a pasta pai.
|
||||
#
|
||||
# Uso:
|
||||
# admin/deploy-codeclass.command # porta 5050 (padrão)
|
||||
# admin/deploy-codeclass.command 8080 # porta customizada
|
||||
#
|
||||
# O que faz:
|
||||
# 1) localiza a toolchain Swift (Xcode) e compila o codeclass (swift build)
|
||||
# 2) derruba qualquer instancia antiga na porta de destino
|
||||
# 3) sobe o servidor novo em background (TigreApp serve) com as classes
|
||||
# novas (TranscriptionService + AppleVisionAnalyzer)
|
||||
# 4) abre o navegador quando o servidor responder
|
||||
#
|
||||
# Diferente de deploy.command (legado Python/Flask em code/app.py), este
|
||||
# script sobe o servidor do codeclass/ (100% Swift). Na porta 5050 eles se
|
||||
# conflitam: rode APENAS UM deles por vez.
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
PORT="${1:-5050}"
|
||||
CLASSDIR="$PROJECT_DIR/codeclass"
|
||||
|
||||
echo "============================================================"
|
||||
echo " Tigre (codeclass) :: Deploy do servidor web novo (Swift)"
|
||||
echo " Pasta: $PROJECT_DIR"
|
||||
echo " Porta: $PORT"
|
||||
echo "============================================================"
|
||||
echo
|
||||
|
||||
# 1) Toolchain Swift (Xcode 26 / swift 6.3). Respeita PATH se ja configurado.
|
||||
SWIFT="$(command -v swift || true)"
|
||||
if [ -z "$SWIFT" ] && [ -x "/Volumes/Merongo/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift" ]; then
|
||||
export PATH="/Volumes/Merongo/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin:$PATH"
|
||||
SWIFT="swift"
|
||||
fi
|
||||
if [ -z "$SWIFT" ]; then
|
||||
echo "ERRO: swift nao encontrado no PATH nem no Xcode padrao."
|
||||
echo " Instale Xcode 26+ ou exporte o PATH da toolchain."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2) Compilar o codeclass
|
||||
echo "[1/3] Compilando o codeclass (swift build)..."
|
||||
cd "$CLASSDIR"
|
||||
"$SWIFT" build 2>&1 | tail -5
|
||||
echo " Build OK."
|
||||
echo
|
||||
|
||||
# 3) Derrubar instancias antigas na porta de destino
|
||||
echo "[2/3] Parando instancia anterior na porta $PORT..."
|
||||
lsof -ti:"$PORT" | xargs kill -9 2>/dev/null || true
|
||||
sleep 1
|
||||
true
|
||||
|
||||
# 4) Subir o servidor novo em background (nao trava o terminal)
|
||||
echo "[3/3] Levantando o servidor novo (TigreApp serve)..."
|
||||
echo " http://localhost:$PORT"
|
||||
echo
|
||||
LOG="/tmp/tigre-codeclass-web.log"
|
||||
nohup "$SWIFT" run TigreApp serve --port "$PORT" --project-root "$PROJECT_DIR" > "$LOG" 2>&1 &
|
||||
SERVER_PID=$!
|
||||
echo " PID do servidor: $SERVER_PID (log: $LOG)"
|
||||
echo
|
||||
|
||||
# Abre o navegador padrao assim que o servidor responder (nao trava o boot)
|
||||
(
|
||||
for _ in $(seq 1 45); do
|
||||
if curl -s -o /dev/null "http://localhost:$PORT"; then
|
||||
open "http://localhost:$PORT"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
) &
|
||||
|
||||
# Mostra o log por alguns segundos para confirmar o boot
|
||||
sleep 5
|
||||
echo "--- primeiras linhas do log ---"
|
||||
tail -10 "$LOG"
|
||||
echo
|
||||
echo "============================================================"
|
||||
echo " Servidor novo (codeclass) no ar em http://localhost:$PORT"
|
||||
echo " Para encerrar: lsof -ti:$PORT | xargs kill -9"
|
||||
echo "============================================================"
|
||||
Executable
+110
@@ -0,0 +1,110 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Doza Assist - Deploy (Graphify + Servidor)
|
||||
# Sobe um nivel: o script vive em admin/, o projeto e a pasta pai
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
echo "============================================================"
|
||||
echo " Doza Assist :: Deploy - Graphify + Servidor"
|
||||
echo " Pasta: $(pwd)"
|
||||
echo "============================================================"
|
||||
echo
|
||||
|
||||
# 1) Resolve quem executa o graphify (preferencia: uv, depois graphify no PATH)
|
||||
if command -v uv >/dev/null 2>&1; then
|
||||
RUN="uv tool run --from graphifyy graphify"
|
||||
elif command -v graphify >/dev/null 2>&1; then
|
||||
RUN="graphify"
|
||||
else
|
||||
echo "graphify nao encontrado. Instalando via pip..."
|
||||
python3 -m pip install -q --upgrade graphifyy
|
||||
RUN="graphify"
|
||||
fi
|
||||
|
||||
# 2) Roda o graphify SEMPRE (deep + update; code-only se nao houver API key)
|
||||
ARGS="--mode deep --update"
|
||||
if [ -n "$GEMINI_API_KEY" ] || [ -n "$GOOGLE_API_KEY" ] || [ -n "$MOONSHOT_API_KEY" ] \
|
||||
|| [ -n "$ANTHROPIC_API_KEY" ] || [ -n "$OPENAI_API_KEY" ] || [ -n "$DEEPSEEK_API_KEY" ]; then
|
||||
:
|
||||
else
|
||||
echo "AVISO: nenhuma LLM API key detectada. Usando --code-only (codigo apenas;"
|
||||
echo " docs/imagens serao ignorados). Para extracao semantica completa,"
|
||||
echo " defina GEMINI_API_KEY ou GOOGLE_API_KEY."
|
||||
ARGS="--mode deep --update --code-only"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Executando graphify (DEEP + UPDATE)..."
|
||||
echo
|
||||
$RUN "." $ARGS || echo "[AVISO] graphify falhou; o deploy continua mesmo assim."
|
||||
|
||||
# 2.5) Reindexar o RAG (doza.code_chunks) - so roda se rag/.env estiver
|
||||
# preenchido; requer o tunel SSH do banco aberto (ver rag/SETUP.md).
|
||||
# Best-effort: nunca trava o deploy.
|
||||
echo
|
||||
echo "Reindexando RAG..."
|
||||
PORT=55435
|
||||
export RAG_DB_PORT=$PORT
|
||||
export RAG_DB_HOST=127.0.0.1
|
||||
bash "$PROJECT_DIR/rag/ensure_tunnel.sh" || true
|
||||
RAG_VENV_PY="$PROJECT_DIR/code/.venv/bin/python3"
|
||||
if [ ! -x "$RAG_VENV_PY" ]; then
|
||||
RAG_VENV_PY="python3"
|
||||
fi
|
||||
if [ -f "$PROJECT_DIR/rag/.env" ] && grep -q '^RAG_DB_PASSWORD=.\+' "$PROJECT_DIR/rag/.env"; then
|
||||
"$RAG_VENV_PY" -m pip install -q -r "$PROJECT_DIR/rag/requirements.txt" 2>/dev/null || true
|
||||
"$RAG_VENV_PY" "$PROJECT_DIR/rag/index_code.py" || echo "[AVISO] indexacao RAG falhou (tunel/banco indisponivel?); deploy continua."
|
||||
else
|
||||
echo "[INFO] rag/.env sem RAG_DB_PASSWORD preenchida; pulando indexacao RAG."
|
||||
fi
|
||||
|
||||
# 3) Derrubar qualquer instancia antiga do servidor
|
||||
echo
|
||||
echo "Parando instancias anteriores do servidor..."
|
||||
lsof -ti:5050 | xargs kill -9 2>/dev/null || true
|
||||
pkill -f "python.*app.py" 2>/dev/null || true
|
||||
sleep 1
|
||||
true
|
||||
|
||||
# 4) Levantar o servidor Doza Assist
|
||||
APP_DIR="$PROJECT_DIR/code"
|
||||
cd "$APP_DIR"
|
||||
|
||||
# Ensure Homebrew binaries (ffmpeg, etc.) are on PATH
|
||||
if [ -d "/opt/homebrew/bin" ]; then
|
||||
export PATH="/opt/homebrew/bin:$PATH"
|
||||
elif [ -d "/usr/local/bin" ]; then
|
||||
export PATH="/usr/local/bin:$PATH"
|
||||
fi
|
||||
|
||||
VENV_PY="$APP_DIR/.venv/bin/python3"
|
||||
if [ ! -x "$VENV_PY" ]; then
|
||||
echo "ERRO: ambiente Python do macOS nao encontrado:"
|
||||
echo " $VENV_PY"
|
||||
echo
|
||||
echo "Execute primeiro: admin/install.command"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "============================================================"
|
||||
echo " Levantando o servidor Doza Assist..."
|
||||
echo " http://localhost:5050"
|
||||
echo "============================================================"
|
||||
echo
|
||||
|
||||
# Abre o navegador padrao assim que o servidor responder (nao trava o boot)
|
||||
(
|
||||
for _ in $(seq 1 30); do
|
||||
if curl -s -o /dev/null "http://localhost:5050"; then
|
||||
open "http://localhost:5050"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
) &
|
||||
|
||||
"$VENV_PY" app.py
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Graphify - atualizacao profunda e incremental do projeto
|
||||
# Sobe um nivel: o script vive em admin/, o projeto e a pasta pai
|
||||
SCRIPT_DIR="$(dirname "$0")"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
echo "============================================================"
|
||||
echo " Graphify :: atualizacao profunda e incremental do projeto"
|
||||
echo " Pasta: $(pwd)"
|
||||
echo "============================================================"
|
||||
echo
|
||||
|
||||
# 1) Resolve quem executa o graphify (preferencia: uv, depois graphify no PATH)
|
||||
if command -v uv >/dev/null 2>&1; then
|
||||
RUN="uv tool run --from graphifyy graphify"
|
||||
echo "Runner: uv tool run --from graphifyy graphify"
|
||||
elif command -v graphify >/dev/null 2>&1; then
|
||||
RUN="graphify"
|
||||
echo "Runner: graphify (PATH)"
|
||||
else
|
||||
echo "graphify nao encontrado. Instalando via pip..."
|
||||
python3 -m pip install -q --upgrade graphifyy
|
||||
RUN="graphify"
|
||||
echo "Runner: graphify (apos install)"
|
||||
fi
|
||||
|
||||
# 2) Atualizacao profunda e incremental
|
||||
# --mode deep : extracao semantica agressiva (arestas INFERRED mais ricas)
|
||||
# --update : re-extrai so o que mudou; 1a execucao faz o build completo
|
||||
# Sem LLM API key, cai em --code-only (varre so o codigo, sem docs/imagens)
|
||||
ARGS="--mode deep --update"
|
||||
if [ -n "$GEMINI_API_KEY" ] || [ -n "$GOOGLE_API_KEY" ] || [ -n "$MOONSHOT_API_KEY" ] \
|
||||
|| [ -n "$ANTHROPIC_API_KEY" ] || [ -n "$OPENAI_API_KEY" ] || [ -n "$DEEPSEEK_API_KEY" ]; then
|
||||
:
|
||||
else
|
||||
echo "AVISO: nenhuma LLM API key detectada. Usando --code-only (codigo apenas;"
|
||||
echo " docs/imagens serao ignorados). Para extracao semantica completa,"
|
||||
echo " defina GEMINI_API_KEY ou GOOGLE_API_KEY e rode novamente."
|
||||
ARGS="--mode deep --update --code-only"
|
||||
fi
|
||||
echo
|
||||
echo "Executando graphify em modo DEEP + UPDATE..."
|
||||
echo
|
||||
$RUN "." $ARGS
|
||||
|
||||
echo
|
||||
echo "============================================================"
|
||||
echo " Concluido! Resultados em: $(pwd)/graphify-out"
|
||||
echo " - graph.html (grafo interativo - abra no navegador)"
|
||||
echo " - GRAPH_REPORT.md (relatorio de auditoria)"
|
||||
echo " - graph.json (dados brutos / GraphRAG-ready)"
|
||||
echo "============================================================"
|
||||
echo
|
||||
echo "Dica: torne executavel uma unica vez com: chmod +x graphify-update.command"
|
||||
Executable
+99
@@ -0,0 +1,99 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Doza Assist - Instalador (macOS)
|
||||
# Sobe um nivel: o script vive em admin/, o projeto e a pasta pai
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
CODE_DIR="$PROJECT_DIR/code"
|
||||
VENV_DIR="$CODE_DIR/.venv"
|
||||
|
||||
echo "============================================================"
|
||||
echo " Doza Assist - Instalador"
|
||||
echo " Pasta: $CODE_DIR"
|
||||
echo "============================================================"
|
||||
echo
|
||||
|
||||
# Homebrew no PATH (ffmpeg, python3 via brew, etc.)
|
||||
if [ -d "/opt/homebrew/bin" ]; then
|
||||
export PATH="/opt/homebrew/bin:$PATH"
|
||||
elif [ -d "/usr/local/bin" ]; then
|
||||
export PATH="/usr/local/bin:$PATH"
|
||||
fi
|
||||
|
||||
# 1) Verificar Python
|
||||
echo "[1/4] Verificando Python..."
|
||||
if ! command -v python3 >/dev/null 2>&1; then
|
||||
echo "ERRO: python3 nao encontrado."
|
||||
echo "Instale via Homebrew: brew install python@3.12"
|
||||
echo "Ou baixe de: https://www.python.org/downloads/"
|
||||
exit 1
|
||||
fi
|
||||
PY_VER="$(python3 --version 2>&1 | awk '{print $2}')"
|
||||
echo " Python encontrado: $PY_VER"
|
||||
|
||||
# 2) Criar ambiente virtual
|
||||
echo
|
||||
echo "[2/4] Criando ambiente virtual..."
|
||||
if [ -d "$VENV_DIR" ] && [ ! -x "$VENV_DIR/bin/python3" ]; then
|
||||
# venv existente nao e do macOS (ex.: criado no Windows, tem Scripts/ em vez de bin/)
|
||||
echo " AVISO: existe um .venv incompativel com macOS em:"
|
||||
echo " $VENV_DIR"
|
||||
if [ -f "$VENV_DIR/pyvenv.cfg" ]; then
|
||||
echo " Origem registrada:"
|
||||
sed -n 's/^home = / /p' "$VENV_DIR/pyvenv.cfg"
|
||||
fi
|
||||
echo
|
||||
echo " Para continuar e preciso recriar esse ambiente."
|
||||
echo " O diretorio sera movido para .venv.bak (nada e apagado)."
|
||||
printf " Recriar agora? [s/N] "
|
||||
read -r RESP
|
||||
case "$RESP" in
|
||||
[sS]|[sS][iI][mM]|[yY]|[yY][eE][sS]) ;;
|
||||
*) echo " Cancelado pelo usuario. Nada foi alterado."; exit 1 ;;
|
||||
esac
|
||||
rm -rf "$VENV_DIR.bak"
|
||||
mv "$VENV_DIR" "$VENV_DIR.bak"
|
||||
echo " Ambiente antigo movido para: $VENV_DIR.bak"
|
||||
fi
|
||||
|
||||
if [ ! -d "$VENV_DIR" ]; then
|
||||
python3 -m venv "$VENV_DIR"
|
||||
echo " Ambiente virtual criado."
|
||||
else
|
||||
echo " Ambiente virtual ja existe."
|
||||
fi
|
||||
|
||||
VENV_PY="$VENV_DIR/bin/python3"
|
||||
|
||||
# 3) Instalar dependencias
|
||||
echo
|
||||
echo "[3/4] Instalando dependencias..."
|
||||
"$VENV_PY" -m pip install --upgrade pip >/dev/null 2>&1
|
||||
"$VENV_PY" -m pip install -r "$CODE_DIR/requirements.txt"
|
||||
echo " Dependencias instaladas."
|
||||
|
||||
# 4) Verificar ffmpeg
|
||||
echo
|
||||
echo "[4/4] Verificando ffmpeg..."
|
||||
if ! command -v ffmpeg >/dev/null 2>&1; then
|
||||
echo " ffmpeg nao encontrado."
|
||||
echo " Para instalar via Homebrew: brew install ffmpeg"
|
||||
echo " Ou baixe de: https://ffmpeg.org/download.html"
|
||||
else
|
||||
echo " ffmpeg encontrado: $(command -v ffmpeg)"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "============================================================"
|
||||
echo " Instalacao concluida!"
|
||||
echo
|
||||
echo " Para iniciar o servidor:"
|
||||
echo " cd \"$CODE_DIR\""
|
||||
echo " .venv/bin/python3 app.py"
|
||||
echo
|
||||
echo " Ou execute: admin/deploy.command"
|
||||
echo
|
||||
echo " Acesse: http://localhost:5050"
|
||||
echo "============================================================"
|
||||
echo
|
||||
Executable
+155
@@ -0,0 +1,155 @@
|
||||
#!/bin/bash
|
||||
# Tigre (codeclass) - Empacota o TigreAppUI em um bundle .app real.
|
||||
# Sobe um nivel: o script vive em admin/, o projeto e a pasta pai.
|
||||
#
|
||||
# Uso:
|
||||
# admin/make-app.command # build debug + bundle no ~/Applications
|
||||
# TIGRE_APP_CONFIG=release admin/... # build release (otimizado)
|
||||
# TIGRE_APP_DIR=/tmp/Tigre.app admin/... # bundle em outro local
|
||||
#
|
||||
# O que faz:
|
||||
# 1) compila o TigreAppUI (swift build)
|
||||
# 2) gera o icone do app (make_icon.py + sips + iconutil -> AppIcon.icns)
|
||||
# 3) monta ~/Applications/Tigre.app:
|
||||
# Tigre.app/
|
||||
# └── Contents/
|
||||
# ├── Info.plist
|
||||
# ├── MacOS/Tigre (binario compilado)
|
||||
# └── Resources/AppIcon.icns
|
||||
# 4) codesign ad-hoc (evita atrito com Gatekeeper/LaunchServices)
|
||||
#
|
||||
# NÃO instala nem desinstala nada no sistema: um .app e so uma pasta; cada
|
||||
# execucao SOBRESCREVE o bundle. "Desinstalar" = apagar a pasta.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
CLASSDIR="$PROJECT_DIR/codeclass"
|
||||
APP_NAME="Tigre"
|
||||
APP_DIR="${TIGRE_APP_DIR:-$HOME/Applications/$APP_NAME.app}"
|
||||
CONFIG="${TIGRE_APP_CONFIG:-debug}"
|
||||
|
||||
CONTENTS="$APP_DIR/Contents"
|
||||
MACOS_DIR="$CONTENTS/MacOS"
|
||||
RES_DIR="$CONTENTS/Resources"
|
||||
|
||||
echo "============================================================"
|
||||
echo " Tigre :: Empacotando $APP_NAME.app"
|
||||
echo " Config: $CONFIG"
|
||||
echo " Destino: $APP_DIR"
|
||||
echo "============================================================"
|
||||
echo
|
||||
|
||||
# 1) Toolchain Swift (Xcode 26 / swift 6.3). Respeita PATH se ja configurado.
|
||||
SWIFT="$(command -v swift || true)"
|
||||
if [ -z "$SWIFT" ] && [ -x "/Volumes/Merongo/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift" ]; then
|
||||
export PATH="/Volumes/Merongo/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin:$PATH"
|
||||
SWIFT="swift"
|
||||
fi
|
||||
if [ -z "$SWIFT" ]; then
|
||||
echo "ERRO: swift nao encontrado no PATH nem no Xcode padrao."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2) Compilar o codeclass (ida incremental; release e mais lento)
|
||||
echo "[1/4] Compilando o codeclass ($CONFIG)..."
|
||||
cd "$CLASSDIR"
|
||||
STDOUT_LOG="/tmp/tigre-build.log"
|
||||
if "$SWIFT" build -c "$CONFIG" > "$STDOUT_LOG" 2>&1; then
|
||||
tail -3 "$STDOUT_LOG" | sed 's/^/ /' || true
|
||||
else
|
||||
echo " ERRO de compilacao. Ultimas linhas:"
|
||||
tail -20 "$STDOUT_LOG" | sed 's/^/ /'
|
||||
exit 1
|
||||
fi
|
||||
BIN="$CLASSDIR/.build/$CONFIG/TigreAppUI"
|
||||
if [ ! -x "$BIN" ]; then
|
||||
echo "ERRO: binario nao encontrado apos build: $BIN"
|
||||
exit 1
|
||||
fi
|
||||
echo " Build OK."
|
||||
echo
|
||||
|
||||
# 3) Icone do app
|
||||
# Fonte: app-icon.png na RAIZ do projeto (prioridade), depois admin/app-icon.png
|
||||
# (compat), e fallback make_icon.py (gera o "T" laranja sintetico).
|
||||
echo "[2/4] Gerando icone (AppIcon.icns)..."
|
||||
CUSTOM_ICON=""
|
||||
for _cand in "$PROJECT_DIR/app-icon.png" "$SCRIPT_DIR/app-icon.png"; do
|
||||
if [ -f "$_cand" ]; then
|
||||
CUSTOM_ICON="$_cand"
|
||||
break
|
||||
fi
|
||||
done
|
||||
ICON_PNG="/tmp/tigre_icon_1024.png"
|
||||
if [ -n "$CUSTOM_ICON" ]; then
|
||||
echo " Usando icone customizado ($(echo "$CUSTOM_ICON" | sed "s|$PROJECT_DIR|.|"), $(sips -g pixelWidth "$CUSTOM_ICON" 2>/dev/null | awk '/pixelWidth/{print $2}')px)"
|
||||
# Resample p/ 1024 (o iconutil exige o slot 512@2x = 1024) e deriva os demais
|
||||
sips -z 1024 1024 "$CUSTOM_ICON" --out "$ICON_PNG" >/dev/null 2>&1
|
||||
else
|
||||
echo " app-icon.png ausente — usando icone padrao (make_icon.py)"
|
||||
python3 "$SCRIPT_DIR/make_icon.py" "$ICON_PNG"
|
||||
fi
|
||||
ICONSET="/tmp/Tigre.iconset"
|
||||
rm -rf "$ICONSET"
|
||||
mkdir -p "$ICONSET"
|
||||
for sz in 16 32 128 256 512; do
|
||||
sips -z "$sz" "$sz" "$ICON_PNG" --out "$ICONSET/icon_${sz}x${sz}.png" >/dev/null 2>&1
|
||||
dbl=$((sz * 2))
|
||||
sips -z "$dbl" "$dbl" "$ICON_PNG" --out "$ICONSET/icon_${sz}x${sz}@2x.png" >/dev/null 2>&1
|
||||
done
|
||||
iconutil -c icns "$ICONSET" -o "/tmp/TigreAppIcon.icns"
|
||||
rm -rf "$ICONSET" "$ICON_PNG"
|
||||
echo " Icone OK."
|
||||
|
||||
# 4) Montar o bundle .app
|
||||
echo "[3/4] Montando o bundle..."
|
||||
rm -rf "$APP_DIR"
|
||||
mkdir -p "$MACOS_DIR" "$RES_DIR"
|
||||
cp "$BIN" "$MACOS_DIR/$APP_NAME"
|
||||
cp "/tmp/TigreAppIcon.icns" "$RES_DIR/AppIcon.icns"
|
||||
|
||||
cat > "$CONTENTS/Info.plist" <<PLIST
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key>
|
||||
<string>Tigre</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Tigre</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.genial.tigre</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.1.0</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.1.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${APP_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>AppIcon</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>14.0</string>
|
||||
<key>LSUIElement</key>
|
||||
<false/>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.productivity</string>
|
||||
</dict>
|
||||
</plist>
|
||||
PLIST
|
||||
|
||||
echo " Bundle montado."
|
||||
|
||||
# 5) Assinatura ad-hoc (sem identity de Developer ID, suficiente p/ uso local)
|
||||
echo "[4/4] Assinatura ad-hoc..."
|
||||
codesign --force --deep -s - "$APP_DIR" 2>&1 | sed 's/^/ /' || true
|
||||
echo " Pronto: $APP_DIR"
|
||||
echo
|
||||
echo " Para lançar: open \"$APP_DIR\""
|
||||
echo " Para remover: rm -rf \"$APP_DIR\""
|
||||
echo
|
||||
Executable
+146
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Gera o ícone do Tigre (app GUI SwiftUI) em 1024x1024 PNG.
|
||||
|
||||
Desenho: fundo escuro arredondado (gradiente), um "T" em laranja com
|
||||
listras diagonais escuras (pele de tigre). A geração dos tamanhos menores
|
||||
e do .icns é feita pelo make-app.command via sips + iconutil.
|
||||
|
||||
Uso:
|
||||
python3 admin/make_icon.py [caminho_de_saida.png]
|
||||
"""
|
||||
|
||||
import struct
|
||||
import zlib
|
||||
import sys
|
||||
import os
|
||||
|
||||
SIZE = 1024
|
||||
|
||||
|
||||
def create_png(width, height, pixels):
|
||||
"""Cria um PNG RGBA."""
|
||||
def chunk(ctype, data):
|
||||
c = ctype + data
|
||||
crc = struct.pack('>I', zlib.crc32(c) & 0xFFFFFFFF)
|
||||
return struct.pack('>I', len(data)) + c + crc
|
||||
header = b'\x89PNG\r\n\x1a\n'
|
||||
ihdr = chunk(b'IHDR', struct.pack('>IIBBBBB', width, height, 8, 6, 0, 0, 0))
|
||||
raw = b''
|
||||
for y in range(height):
|
||||
raw += b'\x00'
|
||||
for x in range(width):
|
||||
i = (y * width + x) * 4
|
||||
raw += bytes(pixels[i:i+4])
|
||||
idat = chunk(b'IDAT', zlib.compress(raw, 9))
|
||||
return header + ihdr + idat + chunk(b'IEND', b'')
|
||||
|
||||
|
||||
def lerp(a, b, t):
|
||||
return int(a + (b - a) * t)
|
||||
|
||||
|
||||
def draw_icon(size):
|
||||
px = [0] * (size * size * 4)
|
||||
|
||||
def set_px(x, y, r, g, b, a=255):
|
||||
if 0 <= x < size and 0 <= y < size:
|
||||
i = (y * size + x) * 4
|
||||
af = a / 255.0
|
||||
px[i] = lerp(px[i], r, af)
|
||||
px[i+1] = lerp(px[i+1], g, af)
|
||||
px[i+2] = lerp(px[i+2], b, af)
|
||||
px[i+3] = max(px[i+3], a)
|
||||
|
||||
def in_rrect(x, y, x0, y0, x1, y1, r):
|
||||
"""Ponto dentro de retângulo arredondado (sem AA — ok, sips suaviza)."""
|
||||
if x < x0 or x > x1 or y < y0 or y > y1:
|
||||
return False
|
||||
cx = min(max(x, x0 + r), x1 - r)
|
||||
cy = min(max(y, y0 + r), y1 - r)
|
||||
return (x - cx) ** 2 + (y - cy) ** 2 <= r * r
|
||||
|
||||
def fill_rrect(x0, y0, x1, y1, r, rg, g_, b_, a=255):
|
||||
for y in range(int(y0), int(y1)):
|
||||
for x in range(int(x0), int(x1)):
|
||||
if in_rrect(x, y, x0, y0, x1, y1, r):
|
||||
set_px(x, y, rg, g_, b_, a)
|
||||
|
||||
# ── Fundo: gradiente vertical escuro + cantos arredondados (squircle liso) ──
|
||||
m = size * 0.03
|
||||
corner = size * 0.22
|
||||
for y in range(size):
|
||||
t = y / size
|
||||
br = lerp(0x2b, 0x12, t)
|
||||
bgc = lerp(0x2b, 0x12, t)
|
||||
bb = lerp(0x33, 0x16, t)
|
||||
for x in range(size):
|
||||
if in_rrect(x, y, m, m, size - m - 1, size - m - 1, corner):
|
||||
set_px(x, y, br, bgc, bb)
|
||||
|
||||
# ── Letra "T" (barra + haste) em laranja com gradiente ──
|
||||
t_x0, t_x1 = size * 0.16, size * 0.84
|
||||
bar_y0, bar_y1 = size * 0.28, size * 0.47
|
||||
stem_x0, stem_x1 = size * 0.42, size * 0.58
|
||||
stem_y0, stem_y1 = size * 0.47, size * 0.78
|
||||
lr = size * 0.04
|
||||
|
||||
for y in range(size):
|
||||
t = y / size
|
||||
or_ = lerp(0xff, 0xee, t)
|
||||
og = lerp(0xa2, 0x5c, t)
|
||||
ob = lerp(0x38, 0x0d, t)
|
||||
# barra
|
||||
for x in range(size):
|
||||
if in_rrect(x, y, t_x0, bar_y0, t_x1, bar_y1, lr):
|
||||
set_px(x, y, or_, og, ob)
|
||||
# haste
|
||||
for x in range(size):
|
||||
if in_rrect(x, y, stem_x0, stem_y0, stem_x1, stem_y1, lr):
|
||||
if x < stem_x0 + lr or x > stem_x1 - lr or y > stem_y1 - lr:
|
||||
if in_rrect(x, y, stem_x0, stem_y0, stem_x1, stem_y1, lr):
|
||||
set_px(x, y, or_, og, ob)
|
||||
else:
|
||||
set_px(x, y, or_, og, ob)
|
||||
|
||||
# ── Listras de tigre (faixas diagonais escuras) ──
|
||||
stripes = [
|
||||
(0.20, 0.36, 0.58, 0.76), # haste (diagonal)
|
||||
(0.34, 0.50, 0.26, 0.42),
|
||||
(0.50, 0.66, 0.22, 0.40),
|
||||
(0.22, 0.40, 0.62, 0.82),
|
||||
]
|
||||
# Converte faixa reta em diagonal: x' = x + (y - bar_y0) * slant
|
||||
slant = 0.35
|
||||
for (x0f, x1f, y0f, y1f) in stripes:
|
||||
x0, x1 = size * x0f, size * x1f
|
||||
y0, y1 = size * y0f, size * y1f
|
||||
for y in range(int(y0), int(y1)):
|
||||
shift = (y - y0) * slant
|
||||
if shift < 0:
|
||||
continue
|
||||
sx0, sx1 = x0 + shift, x1 + shift
|
||||
for x in range(int(sx0), int(sx1)):
|
||||
if x < 0 or x >= size:
|
||||
continue
|
||||
i = (y * size + x) * 4
|
||||
# cor escura translúcida (mistura manual p/ lerp com canal alpha)
|
||||
af = 0.78
|
||||
px[i] = lerp(px[i], 0x0c, af)
|
||||
px[i+1] = lerp(px[i+1], 0x0a, af)
|
||||
px[i+2] = lerp(px[i+2], 0x06, af)
|
||||
px[i+3] = max(px[i+3], int(255 * 0.85))
|
||||
|
||||
return px
|
||||
|
||||
|
||||
def main():
|
||||
out = sys.argv[1] if len(sys.argv) > 1 else "/tmp/tigre_icon_1024.png"
|
||||
px = draw_icon(SIZE)
|
||||
with open(out, "wb") as f:
|
||||
f.write(create_png(SIZE, SIZE, px))
|
||||
print(f"OK: {out} ({SIZE}x{SIZE})")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compara o pixel medio de dois PNGs (palette grossa, suficiente p/ conferir se o icns e o mesmo desenho)."""
|
||||
import struct
|
||||
import sys
|
||||
import zlib
|
||||
|
||||
|
||||
def read_png(path):
|
||||
d = open(path, 'rb').read()
|
||||
assert d[:8] == b'\x89PNG\r\n\x1a\n', f'nao e PNG: {path}'
|
||||
pos, w, h = 8, None, None
|
||||
idat = b''
|
||||
while pos < len(d):
|
||||
ln = struct.unpack('>I', d[pos:pos+4])[0]
|
||||
typ = d[pos+4:pos+8]
|
||||
data = d[pos+8:pos+8+ln]
|
||||
if typ == b'IHDR':
|
||||
w, h = struct.unpack('>II', data[:8])
|
||||
elif typ == b'IDAT':
|
||||
idat += data
|
||||
pos += 12 + ln
|
||||
if typ == b'IEND':
|
||||
break
|
||||
raw = zlib.decompress(idat)
|
||||
assert w is not None
|
||||
stride = w * 4 + 1
|
||||
prev = bytearray(w * 4)
|
||||
out = bytearray()
|
||||
for y in range(h):
|
||||
row = bytearray(raw[y*stride:(y+1)*stride])
|
||||
f = row[0]
|
||||
line = bytearray(row[1:])
|
||||
for x in range(w):
|
||||
for k in range(4):
|
||||
i = x * 4 + k
|
||||
a = line[i-4] if x > 0 and k < 3 else 0 # aprox: usa vizinho p/ filtros
|
||||
b = prev[i]
|
||||
c = line[i-4] if x > 0 else 0
|
||||
if f == 1:
|
||||
v = (line[i] + (line[i-4] if x > 0 else 0)) & 255
|
||||
elif f == 2:
|
||||
v = (line[i] + prev[i]) & 255
|
||||
elif f == 3:
|
||||
v = (line[i] + ((a + b) >> 1)) & 255
|
||||
elif f == 4:
|
||||
p = a + b - c
|
||||
pa, pb, pc = abs(p-a), abs(p-b), abs(p-c)
|
||||
pr = a if (pa <= pb and pa <= pc) else (b if pb <= pc else c)
|
||||
v = (line[i] + pr) & 255
|
||||
else:
|
||||
v = line[i]
|
||||
line[i] = v
|
||||
out += line
|
||||
prev = line
|
||||
return w, h, out
|
||||
|
||||
|
||||
def average(path):
|
||||
w, h, px = read_png(path)
|
||||
n = w * h
|
||||
r = sum(px[i] for i in range(0, len(px), 4)) // n
|
||||
g = sum(px[i] for i in range(1, len(px), 4)) // n
|
||||
b = sum(px[i] for i in range(2, len(px), 4)) // n
|
||||
opaque = sum(1 for i in range(3, len(px), 4) if px[i] > 200) * 100 // n
|
||||
return w, h, r, g, b, f'{opaque}% opaco'
|
||||
|
||||
|
||||
def main():
|
||||
icns_png = sys.argv[1] # 1024 extraido do .icns
|
||||
orig_png = sys.argv[2] # PNG original do usuario
|
||||
a = average(icns_png)
|
||||
b = average(orig_png)
|
||||
print(f'icns 1024 -> {a}')
|
||||
print(f'original -> {b}')
|
||||
# Mesmo desenho se cores medias proximas e ambos coloridos/transparentes com padrao similar
|
||||
close = abs(a[2]-b[2]) <= 24 and abs(a[3]-b[3]) <= 24 and abs(a[4]-b[4]) <= 24
|
||||
print('MATCH (mesmo desenho)' if close else 'DIFF — verificar manualmente')
|
||||
sys.exit(0 if close else 1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Executable
+112
@@ -0,0 +1,112 @@
|
||||
#!/bin/bash
|
||||
# Tigre (codeclass) - App GUI SwiftUI: build + empacota .app + abre.
|
||||
# Sobe um nivel: o script vive em admin/, o projeto e a pasta pai.
|
||||
#
|
||||
# Uso:
|
||||
# admin/run-app.command # build + indexa RAG + abre app
|
||||
# TIGRE_SKIP_RAG_INDEX=1 admin/run-app.command # pula a reindexação do RAG
|
||||
# TIGRE_APP_CONFIG=release admin/run-app.command# build release (otimizado)
|
||||
#
|
||||
# O que faz:
|
||||
# 1) build do TigreAppUI (via make-app.command) e monta o bundle Tigre.app
|
||||
# em ~/Applications/Tigre.app — um .app no macOS é uma pasta; a cada run
|
||||
# nós SOBRESCREVEMOS o bundle (sem "instalar/desinstalar" nada no sistema).
|
||||
# 2) reindexa o RAG do codeclass (rag/index_tigre.sh) — o build acabou de
|
||||
# atualizar Sources/; pode ser pulado com TIGRE_SKIP_RAG_INDEX=1
|
||||
# 3) finaliza instancia anterior do app GUI, se houver
|
||||
# 4) abre o Tigre.app com `open` → macOS registra no LaunchServices: o app
|
||||
# aparece no Dock com ícone, Cmd+Tab, menu no topo e nome "Tigre".
|
||||
#
|
||||
# Nao conflita com deploy-codeclass.command (servidor web na porta 5050) —
|
||||
# os dois podem rodar juntos e enxergam os mesmos projetos.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
CLASSDIR="$PROJECT_DIR/codeclass"
|
||||
APP_DIR="${TIGRE_APP_DIR:-$HOME/Applications/Tigre.app}"
|
||||
APP_BIN="$APP_DIR/Contents/MacOS/Tigre"
|
||||
LOG="/tmp/tigre-appui.log"
|
||||
|
||||
echo "============================================================"
|
||||
echo " Tigre (codeclass) :: App GUI SwiftUI"
|
||||
echo " Pasta: $PROJECT_DIR"
|
||||
echo " App bundle: $APP_DIR"
|
||||
echo "============================================================"
|
||||
echo
|
||||
|
||||
# 1) Empacotar/atualizar o bundle .app (faz o swift build internamente)
|
||||
echo "[1/3] Build + bundle .app (make-app.command)..."
|
||||
"$SCRIPT_DIR/make-app.command" || exit 1
|
||||
echo
|
||||
|
||||
# 2) Reindexar o RAG do codeclass (rag_tigre) — passo best effort: falha de
|
||||
# indexação (ex.: túnel SSH fora) NÃO impede o app de subir.
|
||||
RAG_INDEXER="$PROJECT_DIR/rag/index_tigre.sh"
|
||||
RAG_LOG="/tmp/tigre-rag-index.log"
|
||||
if [ "${TIGRE_SKIP_RAG_INDEX:-0}" = "1" ]; then
|
||||
echo "[2/3] Reindexação do RAG pulada (TIGRE_SKIP_RAG_INDEX=1)."
|
||||
elif [ ! -x "$RAG_INDEXER" ]; then
|
||||
echo "[2/3] AVISO: rag/index_tigre.sh não encontrado — reindexação pulada."
|
||||
else
|
||||
echo "[2/3] Reindexando o RAG do codeclass (rag/index_tigre.sh)..."
|
||||
if "$RAG_INDEXER" > "$RAG_LOG" 2>&1; then
|
||||
grep -E 'OK:|gravado' "$RAG_LOG" | tail -2 | sed 's/^/ /'
|
||||
echo " RAG indexado."
|
||||
else
|
||||
echo " AVISO: reindexação do RAG falhou (o app segue). Log: $RAG_LOG"
|
||||
tail -3 "$RAG_LOG" | sed 's/^/ /'
|
||||
fi
|
||||
fi
|
||||
echo
|
||||
|
||||
# 3) Encerrar instancia anterior do app GUI — tanto do bundle (.app) quanto do
|
||||
# binario solto do .build (debug/release), senao sobram instancias antigas
|
||||
# vivas e o `open -n` abre uma nova por cima.
|
||||
echo "[3/3] Encerrando instâncias anteriores do Tigre (se houver)..."
|
||||
pkill -f "$APP_BIN" 2>/dev/null || true # instancia do bundle .app
|
||||
pkill -f "TigreAppUI" 2>/dev/null || true # binario solto do .build (debug/release)
|
||||
pkill -x "Tigre" 2>/dev/null || true # qualquer processo "Tigre" restante
|
||||
# Espera o processo encerrar de verdade (SIGTERM pode ser ignorado pelo app)
|
||||
for _ in 1 2 3 4 5; do
|
||||
pgrep -f "TigreAppUI|$APP_BIN" >/dev/null 2>&1 || break
|
||||
sleep 1
|
||||
done
|
||||
# Forca a saida se ainda estiver vivo
|
||||
pkill -9 -f "$APP_BIN" 2>/dev/null || true
|
||||
pkill -9 -f "TigreAppUI" 2>/dev/null || true
|
||||
pkill -9 -x "Tigre" 2>/dev/null || true
|
||||
sleep 1
|
||||
true
|
||||
|
||||
# 4) Abrir o app via LaunchServices (`open`) — não é nohup de binário solto.
|
||||
# TIGRE_PROJECT_ROOT garante que o app ache o puente Python do legado.
|
||||
echo " Abrindo o Tigre.app..."
|
||||
TIGRE_PROJECT_ROOT="$PROJECT_DIR" open -n "$APP_DIR"
|
||||
echo
|
||||
|
||||
# Confere se o processo do bundle subiu
|
||||
sleep 3
|
||||
if pgrep -f "$APP_BIN" >/dev/null 2>&1; then
|
||||
echo "============================================================"
|
||||
echo " Tigre no ar. Ele aparece no Dock com icon e nome 'Tigre'."
|
||||
echo " Para encerrar: Cmd+Q no app | pkill -f '$APP_BIN'"
|
||||
echo " Para remover: rm -rf '$APP_DIR'"
|
||||
echo "============================================================"
|
||||
else
|
||||
echo "============================================================"
|
||||
echo " AVISO: nao detectamos o processo do app. Log do ultimo launch:"
|
||||
echo "------------------------------------------------------------"
|
||||
tail -20 "$LOG" 2>/dev/null || true
|
||||
echo "============================================================"
|
||||
fi
|
||||
|
||||
# Fecha a janela do Terminal que rodou o .command (padrao G-ART run_app):
|
||||
# o app ja esta desacoplado, o script so encerra antes do aviso de
|
||||
# "finalizar processos nesta janela".
|
||||
(
|
||||
sleep 2
|
||||
osascript -e 'tell application "Terminal" to close (every window whose name contains "run-app")' >/dev/null 2>&1
|
||||
) &
|
||||
exit 0
|
||||
Executable
+67
@@ -0,0 +1,67 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Doza Assist - Testes automatizados (macOS)
|
||||
# Sobe um nivel: o script vive em admin/, o projeto e a pasta pai
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
APP_DIR="$PROJECT_DIR/code"
|
||||
VENV_PY="$APP_DIR/.venv/bin/python3"
|
||||
|
||||
# Cores (so quando a saida e um terminal)
|
||||
if [ -t 1 ]; then
|
||||
C_CYAN=$'\033[36m'; C_GREEN=$'\033[32m'; C_RED=$'\033[31m'
|
||||
C_YELLOW=$'\033[33m'; C_OFF=$'\033[0m'
|
||||
else
|
||||
C_CYAN=''; C_GREEN=''; C_RED=''; C_YELLOW=''; C_OFF=''
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "${C_CYAN}============================================================${C_OFF}"
|
||||
echo "${C_CYAN} DOZA ASSIST - TESTES AUTOMATIZADOS${C_OFF}"
|
||||
echo "${C_CYAN}============================================================${C_OFF}"
|
||||
echo
|
||||
|
||||
if [ ! -x "$VENV_PY" ]; then
|
||||
echo "${C_RED}ERRO: ambiente Python nao encontrado:${C_OFF}"
|
||||
echo "${C_YELLOW}$VENV_PY${C_OFF}"
|
||||
echo
|
||||
echo "Execute primeiro a instalacao do projeto para criar a pasta .venv:"
|
||||
echo " admin/install.command"
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$APP_DIR" || exit 1
|
||||
export PYTHONUTF8=1
|
||||
|
||||
# pytest nao consta em requirements.txt; instala sob demanda no venv do projeto
|
||||
if ! "$VENV_PY" -c "import pytest" >/dev/null 2>&1; then
|
||||
echo "${C_YELLOW}pytest nao encontrado no venv. Instalando...${C_OFF}"
|
||||
if ! "$VENV_PY" -m pip install -q pytest; then
|
||||
echo "${C_RED}ERRO: falha ao instalar o pytest.${C_OFF}"
|
||||
exit 1
|
||||
fi
|
||||
echo "${C_GREEN}pytest instalado.${C_OFF}"
|
||||
echo
|
||||
fi
|
||||
|
||||
echo "${C_CYAN}Cada teste aprovado aparecera abaixo como PASSED em verde.${C_OFF}"
|
||||
echo "${C_CYAN}Falhas aparecerao como FAILED em vermelho, com o motivo resumido.${C_OFF}"
|
||||
echo
|
||||
|
||||
"$VENV_PY" -m pytest -vv --tb=short -ra --color=yes "$@"
|
||||
TEST_EXIT=$?
|
||||
|
||||
echo
|
||||
echo "============================================================"
|
||||
if [ "$TEST_EXIT" -eq 0 ]; then
|
||||
echo "${C_GREEN} TODOS OS TESTES FORAM APROVADOS.${C_OFF}"
|
||||
echo "============================================================"
|
||||
echo
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "${C_RED} EXISTEM TESTES COM FALHA. Veja os itens FAILED acima.${C_OFF}"
|
||||
echo "============================================================"
|
||||
echo
|
||||
exit "$TEST_EXIT"
|
||||
Executable
+161
@@ -0,0 +1,161 @@
|
||||
#!/bin/zsh
|
||||
# ============================================================
|
||||
# OpenCut Ronald - admin/update.command
|
||||
#
|
||||
# ATUALIZACOES: deixa a arvore pronta para rodar/commitar.
|
||||
# 1. Sincroniza as dependencias Python do venv de producao
|
||||
# 2. Rebuild dos paineis CEP/UXP (so se a fonte mudou)
|
||||
# 3. Testes (painel + backend)
|
||||
#
|
||||
# Nao commita, nao faz push e nao sobe servidor - isso e trabalho do
|
||||
# commit.command e do deploy.command. Rode-o sozinho quando quiser so
|
||||
# revalidar a arvore, ou deixe o OpenCut.command chamar tudo em ordem.
|
||||
#
|
||||
# Uso:
|
||||
# admin/update.command ciclo completo
|
||||
# SKIP_TESTS=1 admin/update.command pula os testes
|
||||
# SKIP_DEPS=1 admin/update.command pula a sincronia de dependencias
|
||||
# ============================================================
|
||||
set -u
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||
CODE="$ROOT/code"
|
||||
PANEL="$CODE/extension/com.opencut.panel"
|
||||
UXP="$CODE/extension/com.opencut.uxp"
|
||||
|
||||
# venv de producao (o mesmo que start-opencut.sh usa). Um segundo venv em
|
||||
# code/.venv ja existiu e divergiu deste - foi assim que o painel passou a
|
||||
# mostrar "Bridge stopped": o servidor subiu num ambiente sem websockets.
|
||||
VENV="/Volumes/Merongo/Library/Application Support/OpenCut/venv"
|
||||
VENV_PY="$VENV/bin/python"
|
||||
# Python 3.14 currently crashes in native test dependencies on macOS. Use the
|
||||
# repository's Python 3.13 test environment when available; production keeps
|
||||
# using the dedicated venv above.
|
||||
TEST_VENV_PY="$CODE/.venv/bin/python"
|
||||
|
||||
GRN="$(tput setaf 2 2>/dev/null)"; YLW="$(tput setaf 3 2>/dev/null)"
|
||||
RD="$(tput setaf 1 2>/dev/null)"; BLU="$(tput setaf 4 2>/dev/null)"
|
||||
NC="$(tput sgr0 2>/dev/null)"
|
||||
say() { echo "${BLU}== ${NC}$*"; }
|
||||
ok() { echo "${GRN}-> OK ${NC}$*"; }
|
||||
warn() { echo "${YLW}-> Aviso: ${NC}$*"; }
|
||||
die() { echo "${RD}-> Erro: ${NC}$*" >&2; exit 1; }
|
||||
|
||||
FAILED=0
|
||||
|
||||
cd "$ROOT" || die "nao consegui entrar em $ROOT"
|
||||
|
||||
# Match start-opencut.sh so tests write logs/caches to the Merongo runtime and
|
||||
# resolve the security-approved media binaries.
|
||||
export HOME="$CODE/.opencut-runtime/home"
|
||||
export OPENCUT_HOME="$HOME/.opencut"
|
||||
export XDG_CACHE_HOME="$CODE/.opencut-runtime/cache"
|
||||
export XDG_CONFIG_HOME="$CODE/.opencut-runtime/config"
|
||||
export HF_HOME="/Volumes/Merongo/SISTEMAS/MODELOSIA/hf-cache"
|
||||
export WHISPER_MODELS_DIR="/Volumes/Merongo/SISTEMAS/MODELOSIA"
|
||||
export TORCH_HOME="/Volumes/Merongo/SISTEMAS/MODELOSIA/torch-cache"
|
||||
mkdir -p "$HOME" "$OPENCUT_HOME" "$XDG_CACHE_HOME" "$XDG_CONFIG_HOME"
|
||||
|
||||
echo "${BLU}============================================================${NC}"
|
||||
say "update.command - dependencias + build + testes"
|
||||
echo "${BLU}============================================================${NC}"
|
||||
echo
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 1) Dependencias Python
|
||||
# ------------------------------------------------------------
|
||||
if [ "${SKIP_DEPS:-0}" = "1" ]; then
|
||||
ok "SKIP_DEPS=1 - pulando dependencias"
|
||||
elif [ ! -x "$VENV_PY" ]; then
|
||||
warn "venv de producao nao encontrado em $VENV (o disco Merongo esta montado?)"
|
||||
else
|
||||
say "Sincronizando dependencias com code/requirements.txt"
|
||||
if "$VENV_PY" -m pip install -q -r "$CODE/requirements.txt"; then
|
||||
ok "dependencias em dia"
|
||||
else
|
||||
warn "pip install falhou"
|
||||
FAILED=1
|
||||
fi
|
||||
# Guarda-costas do bug do "Bridge stopped": a ponte importa websockets
|
||||
# direto, e sem ele o /ws/start responde 400 e o painel fica parado.
|
||||
if "$VENV_PY" -c "import websockets" >/dev/null 2>&1; then
|
||||
ok "websockets presente (ponte de live-updates pode subir)"
|
||||
else
|
||||
warn "websockets AUSENTE - a ponte de live-updates vai falhar"
|
||||
FAILED=1
|
||||
fi
|
||||
fi
|
||||
echo
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 2) Build dos paineis (so se a fonte mudou)
|
||||
# ------------------------------------------------------------
|
||||
CHANGES="$(git status --porcelain 2>/dev/null || true)"
|
||||
if echo "$CHANGES" | grep -qE 'code/extension/(com\.opencut\.panel|com\.opencut\.uxp)/'; then
|
||||
say "Fonte dos paineis alterada - regenerando build"
|
||||
if ( cd "$PANEL" && npm run build >/dev/null 2>&1 ); then
|
||||
ok "build com.opencut.panel"
|
||||
else
|
||||
warn "build do painel falhou"
|
||||
FAILED=1
|
||||
fi
|
||||
if [ -f "$UXP/package.json" ]; then
|
||||
if ( cd "$UXP" && npm run build >/dev/null 2>&1 ); then
|
||||
ok "build com.opencut.uxp"
|
||||
else
|
||||
warn "build do uxp falhou"
|
||||
FAILED=1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
ok "Fonte dos paineis inalterada - sem rebuild"
|
||||
fi
|
||||
echo
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# 3) Testes
|
||||
# ------------------------------------------------------------
|
||||
if [ "${SKIP_TESTS:-0}" = "1" ]; then
|
||||
ok "SKIP_TESTS=1 - pulando testes"
|
||||
else
|
||||
say "Testes do painel (vitest)"
|
||||
if ( cd "$PANEL" && npm test >/dev/null 2>&1 ); then
|
||||
ok "testes do painel passaram"
|
||||
else
|
||||
warn "testes do painel FALHARAM - rode: (cd \"$PANEL\" && npm test)"
|
||||
FAILED=1
|
||||
fi
|
||||
|
||||
if [ -x "$TEST_VENV_PY" ] || [ -x "$VENV_PY" ]; then
|
||||
say "Testes do backend (pytest)"
|
||||
TEST_PY="$TEST_VENV_PY"
|
||||
[ -x "$TEST_PY" ] || TEST_PY="$VENV_PY"
|
||||
if ! "$TEST_PY" -c "import pytest" >/dev/null 2>&1; then
|
||||
"$TEST_PY" -m pip install -q pytest >/dev/null 2>&1 || true
|
||||
fi
|
||||
if "$TEST_PY" -c "import pytest" >/dev/null 2>&1; then
|
||||
# O backend usa o FFmpeg do projeto (governado pelo gate de CVEs em
|
||||
# start-opencut.sh). Os testes precisam do MESMO PATH, senão pegam o
|
||||
# ffmpeg do sistema (ex: Homebrew 8.1) e caem no FfmpegSecurityError.
|
||||
if [ -x "/Volumes/Merongo/SISTEMAS/OPENCUT-MEDIA/ffmpeg/bin/ffmpeg" ]; then
|
||||
export PATH="/Volumes/Merongo/SISTEMAS/OPENCUT-MEDIA/ffmpeg/bin:$PATH"
|
||||
fi
|
||||
if ( cd "$CODE" && "$TEST_PY" -m pytest -q --tb=short ); then
|
||||
ok "testes do backend passaram"
|
||||
else
|
||||
warn "testes do backend FALHARAM"
|
||||
FAILED=1
|
||||
fi
|
||||
else
|
||||
warn "pytest indisponivel - testes do backend pulados"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo
|
||||
if [ "$FAILED" = "0" ]; then
|
||||
ok "Atualizacao concluida sem pendencias."
|
||||
exit 0
|
||||
fi
|
||||
warn "Atualizacao concluida COM avisos - reveja os itens acima antes de commitar."
|
||||
exit 1
|
||||
Reference in New Issue
Block a user