@danhachuel/thunderbolt 0.3.60 → 0.3.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MANUAL-INSTALACAO.md +6 -6
- package/README.md +2 -2
- package/hermes_ui/influencers.py +12 -6
- package/package.json +1 -1
- package/scripts/cli.mjs +4 -4
- package/scripts/install.mjs +53 -1
package/MANUAL-INSTALACAO.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Este manual descreve a instalação local da UI Thunderbolt, baseada no MoneyPrinterTurbo, utilizando o pacote npm `@danhachuel/thunderbolt`. O fluxo recomendado instala automaticamente o ambiente Python, as dependências da aplicação, as dependências do MoneyPrinterTurbo, o Streamlit e o suporte FFmpeg através de `imageio-ffmpeg`.
|
|
4
4
|
|
|
5
|
-
> **Versão deste manual:** 0.3.
|
|
5
|
+
> **Versão deste manual:** 0.3.61
|
|
6
6
|
> **Pacote npm:** `@danhachuel/thunderbolt`
|
|
7
7
|
> **Porta padrão da UI:** `localhost:3030`
|
|
8
8
|
> **Repositório:** [github.com/DanHachuel/thunderbolt](https://github.com/DanHachuel/thunderbolt)
|
|
@@ -33,7 +33,7 @@ A área **AI Influencers > Personagens** permite criar personagens com nome, bio
|
|
|
33
33
|
|
|
34
34
|
O backend local predefinido é **SQLite**, pelo que Personagens e Geração de Conteúdo IA podem ser usados sem credenciais externas. Abra **Configurações > Configuração API > AI Influencers > Banco de Dados Influencers** para confirmar **SQLite** ou seleccionar **Supabase**. Se seleccionar Supabase, preencha **Supabase Project URL**, **Supabase API key** e **Supabase Storage bucket**, clique em **Testar ligação do backend**, aplique primeiro `seed/references/ai_influencers_schema.sql` no SQL Editor do projecto, exponha as quatro tabelas na Data API e configure permissões/RLS adequadas. O bucket deve existir com o nome configurado, por defeito `ai-influencers`.
|
|
35
35
|
|
|
36
|
-
Se pretender trabalhar sem serviço externo, seleccione **SQLite** no selector da aba **Configuração API > AI Influencers** e indique o caminho do ficheiro. O padrão é `storage/state/ai_influencers.db`; o SQLite cria o schema e guarda os assets em `storage/influencers/`. Apenas um backend é usado de cada vez. A aba **AI Influencers** mostra o estado e permite editar directamente o selector, os campos Supabase e o caminho SQLite.
|
|
36
|
+
Se pretender trabalhar sem serviço externo, seleccione **SQLite** no selector da aba **Configuração API > AI Influencers** e indique o caminho do ficheiro. O padrão é `storage/state/ai_influencers.db`; o SQLite cria o schema e guarda os assets em `storage/influencers/`. Apenas um backend é usado de cada vez. A aba **AI Influencers** mostra o estado e permite editar directamente o selector, os campos Supabase e o caminho SQLite. A base fica na pasta persistente do Thunderbolt, fora da instalação temporária do pacote npm; numa actualização normal, personagens, assets e configurações são preservados. O instalador também procura uma base `ai_influencers.db` válida em instalações anteriores do cache npm e recupera-a sem substituir uma base persistente já existente.
|
|
37
37
|
|
|
38
38
|
Em **AI Influencers > Geração de Conteúdo IA**, utilize as subabas **Imagens**, **Vídeos** e **Motion Control**. Seleccione um personagem, prompt e destinos sociais. A subaba **Imagens** usa o pool de imagem configurado em **Imagem e Video**. A subaba **Vídeos** requer uma imagem inicial e usa o pool de vídeo; escolha o provider/modelo no cartão configurado, sem dependência obrigatória do Veo 3.1. KIE AI, Replicate e FAL AI podem usar tarefas assíncronas; o Thunderbolt consulta o estado e guarda o resultado local antes da revisão. Na Replicate, o campo Modelo deve ser o identificador do modelo ou da versão, e os inputs específicos dependem do modelo configurado.
|
|
39
39
|
|
|
@@ -114,13 +114,13 @@ Execute:
|
|
|
114
114
|
Windows PowerShell ou MobaXterm:
|
|
115
115
|
|
|
116
116
|
```powershell
|
|
117
|
-
npx.cmd --yes --prefer-online @danhachuel/thunderbolt@0.3.
|
|
117
|
+
npx.cmd --yes --prefer-online @danhachuel/thunderbolt@0.3.61 install
|
|
118
118
|
```
|
|
119
119
|
|
|
120
120
|
Linux/macOS:
|
|
121
121
|
|
|
122
122
|
```bash
|
|
123
|
-
npx --yes --prefer-online @danhachuel/thunderbolt@0.3.
|
|
123
|
+
npx --yes --prefer-online @danhachuel/thunderbolt@0.3.61 install
|
|
124
124
|
```
|
|
125
125
|
|
|
126
126
|
A instalação normal é **segura para actualizações**: preserva `storage`, Blueprints, Brandings, configurações e artefactos do utilizador. Remove apenas `.venv`, o clone técnico do MoneyPrinterTurbo e dependências que serão recriadas. Uma pasta antiga sem dados do utilizador, como `C:\Users\<utilizador>\AppData\Local\hermes` da tentativa incompleta, pode ser removida; uma pasta antiga que contenha Blueprints, Brandings ou storage é preservada e apenas avisada no terminal. Feche processos Python, Node, Streamlit e MobaXterm que estejam a usar as pastas antes de executar.
|
|
@@ -128,7 +128,7 @@ A instalação normal é **segura para actualizações**: preserva `storage`, Bl
|
|
|
128
128
|
Se quiser apagar absolutamente tudo de forma intencional, use o comando destrutivo separado:
|
|
129
129
|
|
|
130
130
|
```powershell
|
|
131
|
-
npx.cmd --yes --prefer-online @danhachuel/thunderbolt@0.3.
|
|
131
|
+
npx.cmd --yes --prefer-online @danhachuel/thunderbolt@0.3.61 install --purge-data
|
|
132
132
|
```
|
|
133
133
|
|
|
134
134
|
O parâmetro `--purge-data` apaga Blueprints, Brandings, configurações, storage e artefactos locais. Não o use numa actualização normal.
|
|
@@ -542,7 +542,7 @@ Ao abrir a página, o Thunderbolt não prepara dados públicos, não descarrega
|
|
|
542
542
|
|
|
543
543
|
Os parâmetros da UI são número de clusters entre 2 e 10, suporte mínimo entre 0,01 e 0,50, país, engagement, intervalo de datas e tags, todos dentro da área principal da aba. O núcleo normaliza os dados, calcula engagement, aplica filtros, faz transformação logarítmica e standardização, executa K-Means e calcula itemsets/regras com FP-Growth. Não são apresentados resultados até ao primeiro clique em **Analisar Nichos**; o mesmo botão aplica alterações posteriores aos filtros. Os resultados são DataFrames de clusters, itemsets frequentes, regras de associação e dados analisados; o gráfico de dispersão é criado nativamente com Plotly.
|
|
544
544
|
|
|
545
|
-
As dependências adicionais — `scikit-learn`, `mlxtend`, `plotly`, `seaborn`, `matplotlib` e `kagglehub` — são instaladas pelo procedimento normal de `npx`. Em instalações existentes, execute novamente `npx.cmd --yes --prefer-online @danhachuel/thunderbolt@0.3.
|
|
545
|
+
As dependências adicionais — `scikit-learn`, `mlxtend`, `plotly`, `seaborn`, `matplotlib` e `kagglehub` — são instaladas pelo procedimento normal de `npx`. Em instalações existentes, execute novamente `npx.cmd --yes --prefer-online @danhachuel/thunderbolt@0.3.61 install`; o instalador detecta e reutiliza o que já estiver válido.
|
|
546
546
|
|
|
547
547
|
### Niche Finder Apify
|
|
548
548
|
|
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ A primeira versão implementa a camada UI independente com:
|
|
|
32
32
|
|
|
33
33
|
A área **AI Influencers > Personagens** permite guardar um personagem com nome, biografia, idioma, Instagram Business ID opcional, várias imagens de referência e documentos `.md` ou `.json`. O campo **Idioma** é um selector que reutiliza exactamente a lista, os códigos e os labels visuais de **Pipeline Vídeos > Criação de Vídeos**. Cada asset é validado, sanitizado, deduplicado por SHA-256 e guardado separado do perfil; imagens têm pré-visualização e documentos são mostrados como Markdown ou JSON estruturado.
|
|
34
34
|
|
|
35
|
-
O backend local predefinido é **SQLite**, por isso Personagens e Geração de Conteúdo IA funcionam imediatamente sem credenciais externas. Em **Configurações > Configuração API > AI Influencers > Banco de Dados Influencers**, pode manter **SQLite** ou seleccionar **Supabase** quando quiser uma base remota; nesse caso, configure o **Supabase Project URL**, a **Supabase API key** e o bucket de Storage, aplique `seed/references/ai_influencers_schema.sql` no SQL Editor e confirme Data API/RLS. SQLite cria localmente `storage/state/ai_influencers.db` e `storage/influencers/`; apenas um backend fica activo de cada vez. A aba **AI Influencers** mostra o estado actual e permite editar directamente o selector, as credenciais Supabase, o bucket de Storage e o caminho do SQLite. O botão **Testar ligação do backend** é read-only e não bloqueia a utilização local.
|
|
35
|
+
O backend local predefinido é **SQLite**, por isso Personagens e Geração de Conteúdo IA funcionam imediatamente sem credenciais externas. Em **Configurações > Configuração API > AI Influencers > Banco de Dados Influencers**, pode manter **SQLite** ou seleccionar **Supabase** quando quiser uma base remota; nesse caso, configure o **Supabase Project URL**, a **Supabase API key** e o bucket de Storage, aplique `seed/references/ai_influencers_schema.sql` no SQL Editor e confirme Data API/RLS. SQLite cria localmente `storage/state/ai_influencers.db` e `storage/influencers/`; apenas um backend fica activo de cada vez. A aba **AI Influencers** mostra o estado actual e permite editar directamente o selector, as credenciais Supabase, o bucket de Storage e o caminho do SQLite. O botão **Testar ligação do backend** é read-only e não bloqueia a utilização local. A base SQLite fica na pasta persistente do Thunderbolt, fora da instalação temporária do pacote npm; durante uma actualização normal, personagens, assets e configurações são preservados. Se uma versão anterior tiver guardado `ai_influencers.db` no cache npm, o instalador procura uma cópia válida e recupera-a sem substituir uma base persistente já existente.
|
|
36
36
|
|
|
37
37
|
A página **AI Influencers > Geração de Conteúdo IA** contém as subabas **Imagens**, **Vídeos** e **Motion Control**. Imagens usam os cartões activos do **Pool Imagem**; Vídeos usam os cartões activos do **Pool Vídeo** e exigem uma imagem inicial image-to-video. O selector de modelo não está preso ao Veo 3.1: pode utilizar KIE AI, Replicate, FAL AI, Pollinations ou outro cartão que declare suporte para vídeo. Para Replicate, o campo Modelo deve conter o identificador aceito pela API, como `owner/model` ou `owner/model:version`; a tarefa usa `POST /v1/predictions`, consulta o estado assíncrono e guarda o resultado localmente. A publicação para Instagram, TikTok, YouTube Shorts ou Facebook não é automática: os destinos são registados para revisão e o envio exige uma acção posterior explícita.
|
|
38
38
|
|
|
@@ -79,7 +79,7 @@ No topo da área principal da aplicação existe o menu nativo de idioma no padr
|
|
|
79
79
|
|
|
80
80
|
A UI suporta os temas **Dark** e **Light** através do menu nativo de três pontos do Streamlit, no local original do toolbar. Não existe um selector Theme adicional dentro da página. A configuração distribuída em `.streamlit/config.toml` disponibiliza as variantes nomeadas **Dark** e **Light**, e o menu nativo continua responsável por alternar entre os modos, seguindo o padrão do [MoneyPrinterTurbo](https://github.com/harry0703/MoneyPrinterTurbo). O CSS próprio do Thunderbolt usa cores semânticas, `currentColor` e `color-mix` para acompanhar o tema activo, sem alterar a posição nem a funcionalidade do toolbar, do botão Deploy e do menu principal.
|
|
81
81
|
|
|
82
|
-
## Navegação da UI 0.3.
|
|
82
|
+
## Navegação da UI 0.3.61
|
|
83
83
|
|
|
84
84
|
A barra lateral mantém os níveis principais, nesta ordem: **Início**, **Niche Finder**, **Pipeline**, **Pipeline TikTok**, **Automação**, **Edição**, **AI Influencers** e **Configurações**. **Pipeline Vídeos** é expansível e contém **Criação de Vídeos**, **Backlog Vídeos**, **Roteiros**, **Thumbnails** e **Upload**. **Pipeline Música** é expansível e contém **Criação de Músicas** e **Upload Música**. **Automação** também é expansível e contém **Automação Youtube**. **Edição** é expansível e contém **Limpador de Metadados**, **Cortes**, **Editor Python** e **Download Mídia**, nessa ordem. **AI Influencers** é expansível e contém **Personagens**, **Geração de Conteúdo IA**, **Redes Sociais**, **Tutorial Meta** e **Tutorial Supabase**, nessa ordem. A página Geração de Conteúdo IA divide-se em **Imagens**, **Vídeos** e **Motion Control**; a subaba Motion Control permanece vazia nesta release.
|
|
85
85
|
**Niche Finder** é expansível e contém **Niche Finder Kaggle** e **Niche Finder Apify**. **Configurações** é expansível e contém **Canais Youtube**, **Blueprints Youtube**, **MCP**, **Contas Google**, **Notificações**, **Logs** e **Configuração API**, nessa ordem. O Início reúne o dashboard e as filas do Pipeline, sem botões de acções rápidas.
|
package/hermes_ui/influencers.py
CHANGED
|
@@ -211,14 +211,21 @@ class InfluencerBackendError(RuntimeError):
|
|
|
211
211
|
"""Raised when the selected database backend is unavailable or misconfigured."""
|
|
212
212
|
|
|
213
213
|
|
|
214
|
+
def _resolve_sqlite_path(path: str | Path | None = None) -> Path:
|
|
215
|
+
raw_path = Path(path or (STORAGE / "state" / "ai_influencers.db")).expanduser()
|
|
216
|
+
if raw_path.is_absolute():
|
|
217
|
+
return raw_path.resolve()
|
|
218
|
+
parts = raw_path.parts
|
|
219
|
+
if parts and parts[0].casefold() == "storage":
|
|
220
|
+
raw_path = Path(*parts[1:]) if len(parts) > 1 else Path("state") / "ai_influencers.db"
|
|
221
|
+
return (STORAGE / raw_path).resolve()
|
|
222
|
+
|
|
223
|
+
|
|
214
224
|
class SQLiteInfluencerRepository:
|
|
215
225
|
backend = "SQLite"
|
|
216
226
|
|
|
217
227
|
def __init__(self, path: str | Path | None = None):
|
|
218
|
-
|
|
219
|
-
if not candidate.is_absolute():
|
|
220
|
-
candidate = ROOT / candidate
|
|
221
|
-
self.path = candidate.resolve()
|
|
228
|
+
self.path = _resolve_sqlite_path(path)
|
|
222
229
|
self.path.parent.mkdir(parents=True, exist_ok=True)
|
|
223
230
|
|
|
224
231
|
def ensure_schema(self) -> None:
|
|
@@ -511,8 +518,7 @@ class SupabaseInfluencerRepository:
|
|
|
511
518
|
|
|
512
519
|
def sqlite_path_from_settings(settings: Mapping[str, Any]) -> Path:
|
|
513
520
|
raw = str(settings.get("influencer_sqlite_path") or "storage/state/ai_influencers.db").strip()
|
|
514
|
-
|
|
515
|
-
return path if path.is_absolute() else ROOT / path
|
|
521
|
+
return _resolve_sqlite_path(raw)
|
|
516
522
|
|
|
517
523
|
|
|
518
524
|
def backend_name(settings: Mapping[str, Any]) -> str:
|
package/package.json
CHANGED
package/scripts/cli.mjs
CHANGED
|
@@ -27,11 +27,11 @@ const python = process.env.THUNDERBOLT_PYTHON || process.env.HERMES_PYTHON || (e
|
|
|
27
27
|
const main = resolve(root, "app", "main.py");
|
|
28
28
|
const settingsPath = join(thunderboltHome, "storage", "state", "settings.json");
|
|
29
29
|
|
|
30
|
-
function run(command, commandArgs, label = "comando") {
|
|
30
|
+
function run(command, commandArgs, label = "comando", environment = process.env) {
|
|
31
31
|
console.log(`Thunderbolt: a iniciar ${label}...`);
|
|
32
32
|
const result = spawnSync(command, commandArgs, {
|
|
33
33
|
stdio: "inherit",
|
|
34
|
-
env:
|
|
34
|
+
env: environment,
|
|
35
35
|
cwd: root,
|
|
36
36
|
windowsHide: false,
|
|
37
37
|
});
|
|
@@ -144,11 +144,11 @@ const runtimeEnv = {
|
|
|
144
144
|
};
|
|
145
145
|
|
|
146
146
|
if (args[0] === "worker" || args.includes("--worker")) {
|
|
147
|
-
run(python, ["-m", "hermes_ui.automation_worker", ...args.filter((arg) => arg !== "worker" && arg !== "--worker")], "worker de automação");
|
|
147
|
+
run(python, ["-m", "hermes_ui.automation_worker", ...args.filter((arg) => arg !== "worker" && arg !== "--worker")], "worker de automação", runtimeEnv);
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
if (args[0] === "pipeline-worker" || args.includes("--pipeline-worker")) {
|
|
151
|
-
run(python, ["-m", "hermes_ui.pipeline_worker", ...args.filter((arg) => arg !== "pipeline-worker" && arg !== "--pipeline-worker")], "worker do pipeline de vídeos");
|
|
151
|
+
run(python, ["-m", "hermes_ui.pipeline_worker", ...args.filter((arg) => arg !== "pipeline-worker" && arg !== "--pipeline-worker")], "worker do pipeline de vídeos", runtimeEnv);
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
const port = process.env.THUNDERBOLT_PORT || process.env.HERMES_PORT || "3030";
|
package/scripts/install.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, rmSync, renameSync, cpSync, copyFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, renameSync, cpSync, copyFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { homedir, platform } from "node:os";
|
|
4
4
|
import { basename, dirname, join, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -145,6 +145,57 @@ function copySeedPromptMasters(storageRoot) {
|
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
function copyMissingTree(source, target) {
|
|
149
|
+
if (!existsSync(source)) return 0;
|
|
150
|
+
let copied = 0;
|
|
151
|
+
let sourceStat;
|
|
152
|
+
try { sourceStat = statSync(source); } catch { return 0; }
|
|
153
|
+
if (sourceStat.isDirectory()) {
|
|
154
|
+
mkdirSync(target, { recursive: true });
|
|
155
|
+
for (const entry of readdirSync(source, { withFileTypes: true })) {
|
|
156
|
+
copied += copyMissingTree(join(source, entry.name), join(target, entry.name));
|
|
157
|
+
}
|
|
158
|
+
return copied;
|
|
159
|
+
}
|
|
160
|
+
if (existsSync(target)) return 0;
|
|
161
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
162
|
+
copyFileSync(source, target);
|
|
163
|
+
return 1;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function legacyNpxPackageRoots() {
|
|
167
|
+
const roots = new Set();
|
|
168
|
+
const cacheHashes = [resolve(root, "../../..")];
|
|
169
|
+
const cacheRoot = resolve(root, "../../../..");
|
|
170
|
+
if (existsSync(cacheRoot)) {
|
|
171
|
+
try {
|
|
172
|
+
for (const entry of readdirSync(cacheRoot, { withFileTypes: true })) {
|
|
173
|
+
if (entry.isDirectory()) cacheHashes.push(join(cacheRoot, entry.name));
|
|
174
|
+
}
|
|
175
|
+
} catch { /* cache inacessível: a instalação normal continua */ }
|
|
176
|
+
}
|
|
177
|
+
for (const hashRoot of cacheHashes) {
|
|
178
|
+
const candidate = resolve(hashRoot, "node_modules", "@danhachuel", "thunderbolt");
|
|
179
|
+
if (candidate === root || roots.has(candidate)) continue;
|
|
180
|
+
if (existsSync(join(candidate, "storage", "state", "ai_influencers.db"))) roots.add(candidate);
|
|
181
|
+
}
|
|
182
|
+
return [...roots].sort((left, right) => {
|
|
183
|
+
try { return statSync(join(right, "storage", "state", "ai_influencers.db")).mtimeMs - statSync(join(left, "storage", "state", "ai_influencers.db")).mtimeMs; }
|
|
184
|
+
catch { return 0; }
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function migrateLegacyNpxStorage() {
|
|
189
|
+
const targetStorage = join(thunderboltHome, "storage");
|
|
190
|
+
const candidates = legacyNpxPackageRoots();
|
|
191
|
+
let copied = 0;
|
|
192
|
+
for (const candidate of candidates) {
|
|
193
|
+
copied += copyMissingTree(join(candidate, "storage"), targetStorage);
|
|
194
|
+
if (existsSync(join(targetStorage, "state", "ai_influencers.db"))) break;
|
|
195
|
+
}
|
|
196
|
+
if (copied > 0) console.log(`Dados locais AI Influencers recuperados do cache npm para ${targetStorage}.`);
|
|
197
|
+
}
|
|
198
|
+
|
|
148
199
|
function copyOrMove(source, target, label) {
|
|
149
200
|
if (!existsSync(source) || existsSync(target)) return false;
|
|
150
201
|
mkdirSync(dirname(target), { recursive: true });
|
|
@@ -325,6 +376,7 @@ function main() {
|
|
|
325
376
|
migrateLegacyInstallation();
|
|
326
377
|
cleanInstallationRoots(moneyprinterPath);
|
|
327
378
|
ensureDirs();
|
|
379
|
+
migrateLegacyNpxStorage();
|
|
328
380
|
const python = skipDeps ? null : ensurePython();
|
|
329
381
|
if (!skipMpt) cloneMoneyPrinter(moneyprinterPath);
|
|
330
382
|
if (!skipDeps) installThunderboltDependencies(python);
|