diff --git a/.gitignore b/.gitignore index 8502f08..54d8759 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,4 @@ errors_utf8.txt vault-unseal-keysdefault-vault.tar prompts/sec-bots.md +AGENTS-PROD.md diff --git a/.playwright-mcp/cristo-snapshot.md b/.playwright-mcp/cristo-snapshot.md new file mode 100644 index 0000000..18ca4a1 --- /dev/null +++ b/.playwright-mcp/cristo-snapshot.md @@ -0,0 +1,37 @@ +- generic [ref=e5]: + - generic [ref=e6]: + - generic [ref=e7]: Chat + - generic [ref=e8]: + - button [ref=e9] [cursor=pointer]: + - img [ref=e10] + - button [ref=e11] [cursor=pointer]: + - img [ref=e12] + - button [ref=e14] [cursor=pointer]: + - img [ref=e15] + - generic [ref=e18]: + - generic [ref=e19]: + - main [ref=e20]: + - paragraph [ref=e31]: Olá! Sou o assistente virtual do Santuário Cristo Redentor. Como posso ajudá-lo hoje com informações sobre celebrações, eventos, visitação ou orações? + - contentinfo [ref=e21]: + - generic [ref=e22]: + - button "Agendar Batizado" [ref=e32] [cursor=pointer] + - button "Agendar Casamento" [ref=e33] [cursor=pointer] + - button "Agendar Missa" [ref=e34] [cursor=pointer] + - button "Agendar Peregrinação" [ref=e35] [cursor=pointer] + - button "Pedido de Oração" [ref=e36] [cursor=pointer] + - button "Uso de Imagem" [ref=e37] [cursor=pointer] + - button "Licenciamento" [ref=e38] [cursor=pointer] + - button "Evento/Iluminação" [ref=e39] [cursor=pointer] + - button "Cadastrar Guia" [ref=e40] [cursor=pointer] + - button "Fazer Doação" [ref=e41] [cursor=pointer] + - generic [ref=e23]: + - generic [ref=e24]: + - button "Agent" [ref=e25] [cursor=pointer] + - button "Chat" [ref=e26] [cursor=pointer] + - textbox "Message... (type @ to mention)" [active] [ref=e27] + - button "↑" [ref=e28] [cursor=pointer] + - button "Scroll to bottom": + - img + - generic: + - generic: + - button "View" \ No newline at end of file diff --git a/.playwright-mcp/page-2026-03-31T19-16-51-748Z.yml b/.playwright-mcp/page-2026-03-31T19-16-51-748Z.yml new file mode 100644 index 0000000..8cff2dd --- /dev/null +++ b/.playwright-mcp/page-2026-03-31T19-16-51-748Z.yml @@ -0,0 +1,45 @@ +- generic [ref=e2]: + - generic [ref=e3]: + - img "General Bots" [ref=e6] + - heading "Welcome Back" [level=1] [ref=e7] + - paragraph [ref=e8]: Sign in to your General Bots account + - generic [ref=e10]: + - generic [ref=e11]: + - generic [ref=e12]: + - generic [ref=e13]: Email Address + - generic [ref=e14]: + - img + - textbox "Email Address" [ref=e15]: + - /placeholder: you@example.com + - generic [ref=e16]: + - generic [ref=e17]: Password + - generic [ref=e18]: + - img + - textbox "Password" [ref=e19]: + - /placeholder: •••••••• + - button [ref=e20] [cursor=pointer]: + - img [ref=e21] + - generic [ref=e24]: + - generic [ref=e27] [cursor=pointer]: Remember me + - link "Forgot password?" [ref=e28] [cursor=pointer]: + - /url: /auth/forgot-password + - button "Sign In" [ref=e29] [cursor=pointer]: + - generic [ref=e30]: Sign In + - generic [ref=e32]: or continue with + - generic [ref=e33]: + - button "Google" [ref=e34] [cursor=pointer]: + - img [ref=e35] + - text: Google + - button "Microsoft" [ref=e40] [cursor=pointer]: + - img [ref=e41] + - text: Microsoft + - button "GitHub" [ref=e46] [cursor=pointer]: + - img [ref=e47] + - text: GitHub + - button "Apple" [ref=e49] [cursor=pointer]: + - img [ref=e50] + - text: Apple + - paragraph [ref=e53]: + - text: Don't have an account? + - link "Create account" [ref=e54] [cursor=pointer]: + - /url: /auth/register \ No newline at end of file diff --git a/.playwright-mcp/page-2026-03-31T19-17-49-094Z.yml b/.playwright-mcp/page-2026-03-31T19-17-49-094Z.yml new file mode 100644 index 0000000..cca8113 --- /dev/null +++ b/.playwright-mcp/page-2026-03-31T19-17-49-094Z.yml @@ -0,0 +1,25 @@ +- generic [ref=e5]: + - generic [ref=e6]: + - generic [ref=e7]: Chat + - generic [ref=e8]: + - button [ref=e9] [cursor=pointer]: + - img [ref=e10] + - button [ref=e11] [cursor=pointer]: + - img [ref=e12] + - button [ref=e14] [cursor=pointer]: + - img [ref=e15] + - generic [ref=e18]: + - generic [ref=e19]: + - main [ref=e20] + - contentinfo [ref=e21]: + - generic [ref=e23]: + - generic [ref=e24]: + - button "Agent" [ref=e25] [cursor=pointer] + - button "Chat" [ref=e26] [cursor=pointer] + - textbox "Message... (type @ to mention)" [active] [ref=e27] + - button "↑" [ref=e28] [cursor=pointer] + - button "Scroll to bottom": + - img + - generic: + - generic: + - button "View" \ No newline at end of file diff --git a/.playwright-mcp/page-2026-03-31T19-18-09-643Z.yml b/.playwright-mcp/page-2026-03-31T19-18-09-643Z.yml new file mode 100644 index 0000000..a9803f8 --- /dev/null +++ b/.playwright-mcp/page-2026-03-31T19-18-09-643Z.yml @@ -0,0 +1,111 @@ +- generic [ref=e2]: + - complementary [ref=e3]: + - generic "Home" [ref=e4] [cursor=pointer]: + - img [ref=e5] + - generic "Search" [ref=e8] [cursor=pointer]: + - img [ref=e9] + - generic "Terminal" [ref=e12] [cursor=pointer]: + - img [ref=e13] + - generic "User" [ref=e15] [cursor=pointer]: + - img [ref=e16] + - generic "Apps" [ref=e19] [cursor=pointer]: + - img [ref=e20] + - generic "Settings" [ref=e25] [cursor=pointer]: + - img [ref=e26] + - generic [ref=e29]: + - generic [ref=e30]: + - generic [ref=e31]: + - generic [ref=e32] [cursor=pointer]: + - img [ref=e34] + - generic [ref=e36]: Vibe + - generic [ref=e37] [cursor=pointer]: + - img [ref=e39] + - generic [ref=e44]: CRM + - generic [ref=e45] [cursor=pointer]: + - img [ref=e47] + - generic [ref=e49]: Campaigns + - generic [ref=e50] [cursor=pointer]: + - img [ref=e52] + - generic [ref=e53]: Lists + - generic [ref=e54] [cursor=pointer]: + - img [ref=e56] + - generic [ref=e58]: Templates + - generic [ref=e59] [cursor=pointer]: + - img [ref=e61] + - generic [ref=e64]: Tasks + - generic [ref=e65] [cursor=pointer]: + - img [ref=e67] + - generic [ref=e69]: Chat + - generic [ref=e70] [cursor=pointer]: + - img [ref=e72] + - generic [ref=e74]: Terminal + - generic [ref=e75] [cursor=pointer]: + - img [ref=e77] + - generic [ref=e79]: Explorer + - generic [ref=e80] [cursor=pointer]: + - img [ref=e82] + - generic [ref=e85]: Editor + - generic [ref=e86] [cursor=pointer]: + - img [ref=e88] + - generic [ref=e93]: Designer + - generic [ref=e94] [cursor=pointer]: + - img [ref=e96] + - generic [ref=e99]: BASIC + - generic [ref=e100] [cursor=pointer]: + - img [ref=e102] + - generic [ref=e105]: Browser + - generic [ref=e106]: + - generic [ref=e107]: + - generic [ref=e108]: Chat + - generic [ref=e109]: + - button [ref=e110] [cursor=pointer]: + - img [ref=e111] + - button [ref=e112] [cursor=pointer]: + - img [ref=e113] + - button [ref=e115] [cursor=pointer]: + - img [ref=e116] + - generic [ref=e119]: + - generic [ref=e120]: + - generic: + - generic: Connecting... + - main [ref=e121] + - contentinfo [ref=e122]: + - generic [ref=e124]: + - generic [ref=e125]: + - button "Agent" [ref=e126] [cursor=pointer] + - button "Chat" [ref=e127] [cursor=pointer] + - textbox "Message... (type @ to mention)" [active] [ref=e128] + - button "↑" [ref=e129] [cursor=pointer] + - button "Scroll to bottom": + - img + - generic: + - generic: + - button "View" + - contentinfo [ref=e130]: + - img [ref=e134] [cursor=pointer] + - generic [ref=e136]: + - combobox [ref=e138] [cursor=pointer]: + - option "🎨 Default" + - option "☀️ Light" [selected] + - option "🍊 Orange" + - option "🌃 Cyberpunk" + - option "🌴 Retrowave" + - option "💭 Vapor Dream" + - option "✨ Y2K" + - option "🔲 3D Bevel" + - option "🕹️ Arcade" + - option "🪩 Disco" + - option "🎸 Grunge" + - option "🎺 Jazz" + - option "🌻 Mellow" + - option "🏠 Mid Century" + - option "📷 Polaroid" + - option "📺 Cartoons" + - option "🏖️ Seaside" + - option "⌨️ Typewriter" + - option "📠 Xerox" + - option "📁 XTree" + - button "Sign In" [ref=e139] [cursor=pointer] + - generic [ref=e140]: + - generic [ref=e141]: 00:00 + - generic [ref=e142]: 01/01/2026 \ No newline at end of file diff --git a/.playwright-mcp/page-2026-03-31T19-18-17-256Z.yml b/.playwright-mcp/page-2026-03-31T19-18-17-256Z.yml new file mode 100644 index 0000000..4f5d8b6 --- /dev/null +++ b/.playwright-mcp/page-2026-03-31T19-18-17-256Z.yml @@ -0,0 +1,39 @@ +- generic [ref=e106]: + - generic [ref=e107]: + - generic [ref=e108]: Chat + - generic [ref=e109]: + - button [ref=e110] [cursor=pointer]: + - img [ref=e111] + - button [ref=e112] [cursor=pointer]: + - img [ref=e113] + - button [ref=e115] [cursor=pointer]: + - img [ref=e116] + - generic [ref=e119]: + - generic [ref=e120]: + - main [ref=e121]: + - paragraph [ref=e145]: Olá! Sou o Assistente Virtual da SEAD — Secretaria de Estado da Administração de Sergipe. Estou aqui para ajudá-lo com contracheques, agendamentos no CEAC, perícias médicas, documentos funcionais, cursos da Escola de Governo e muito mais. Como posso te ajudar hoje? + - generic [ref=e158]: 🔍 Detectar Desvios na Folha + - contentinfo [ref=e122]: + - generic [ref=e123]: + - button "📄 Segunda Via de Contracheque" [ref=e146] [cursor=pointer] + - button "📅 Agendar Atendimento no CEAC" [ref=e147] [cursor=pointer] + - button "🩺 Agendar Perícia Médica" [ref=e148] [cursor=pointer] + - button "📋 Solicitar Documentos Funcionais" [ref=e149] [cursor=pointer] + - button "✏️ Atualizar Dados Cadastrais" [ref=e150] [cursor=pointer] + - button "🎓 Cursos da Escola de Governo" [ref=e151] [cursor=pointer] + - button "💰 Informações sobre Benefícios" [ref=e152] [cursor=pointer] + - button "🔍 Consultar Protocolo" [ref=e153] [cursor=pointer] + - button "📞 Falar com a SEAD" [ref=e154] [cursor=pointer] + - button "🔍 Detectar Desvios na Folha" [active] [ref=e155] [cursor=pointer] + - button "❓ Outras Solicitações" [ref=e156] [cursor=pointer] + - generic [ref=e124]: + - generic [ref=e125]: + - button "Agent" [ref=e126] [cursor=pointer] + - button "Chat" [ref=e127] [cursor=pointer] + - textbox "Message... (type @ to mention)" [ref=e128] + - button "↑" [ref=e129] [cursor=pointer] + - button "Scroll to bottom": + - img + - generic: + - generic: + - button "View" \ No newline at end of file diff --git a/.playwright-mcp/page-2026-03-31T19-18-59-132Z.yml b/.playwright-mcp/page-2026-03-31T19-18-59-132Z.yml new file mode 100644 index 0000000..83cdb5d --- /dev/null +++ b/.playwright-mcp/page-2026-03-31T19-18-59-132Z.yml @@ -0,0 +1,109 @@ +- generic [ref=e2]: + - complementary [ref=e3]: + - generic "Home" [ref=e4] [cursor=pointer]: + - img [ref=e5] + - generic "Search" [ref=e8] [cursor=pointer]: + - img [ref=e9] + - generic "Terminal" [ref=e12] [cursor=pointer]: + - img [ref=e13] + - generic "User" [ref=e15] [cursor=pointer]: + - img [ref=e16] + - generic "Apps" [ref=e19] [cursor=pointer]: + - img [ref=e20] + - generic "Settings" [ref=e25] [cursor=pointer]: + - img [ref=e26] + - generic [ref=e29]: + - generic [ref=e30]: + - generic [ref=e31]: + - generic [ref=e32] [cursor=pointer]: + - img [ref=e34] + - generic [ref=e36]: Vibe + - generic [ref=e37] [cursor=pointer]: + - img [ref=e39] + - generic [ref=e44]: CRM + - generic [ref=e45] [cursor=pointer]: + - img [ref=e47] + - generic [ref=e49]: Campaigns + - generic [ref=e50] [cursor=pointer]: + - img [ref=e52] + - generic [ref=e53]: Lists + - generic [ref=e54] [cursor=pointer]: + - img [ref=e56] + - generic [ref=e58]: Templates + - generic [ref=e59] [cursor=pointer]: + - img [ref=e61] + - generic [ref=e64]: Tasks + - generic [ref=e65] [cursor=pointer]: + - img [ref=e67] + - generic [ref=e69]: Chat + - generic [ref=e70] [cursor=pointer]: + - img [ref=e72] + - generic [ref=e74]: Terminal + - generic [ref=e75] [cursor=pointer]: + - img [ref=e77] + - generic [ref=e79]: Explorer + - generic [ref=e80] [cursor=pointer]: + - img [ref=e82] + - generic [ref=e85]: Editor + - generic [ref=e86] [cursor=pointer]: + - img [ref=e88] + - generic [ref=e93]: Designer + - generic [ref=e94] [cursor=pointer]: + - img [ref=e96] + - generic [ref=e99]: BASIC + - generic [ref=e100] [cursor=pointer]: + - img [ref=e102] + - generic [ref=e105]: Browser + - generic [ref=e106]: + - generic [ref=e107]: + - generic [ref=e108]: Chat + - generic [ref=e109]: + - button [ref=e110] [cursor=pointer]: + - img [ref=e111] + - button [ref=e112] [cursor=pointer]: + - img [ref=e113] + - button [ref=e115] [cursor=pointer]: + - img [ref=e116] + - generic [ref=e119]: + - generic [ref=e120]: + - main [ref=e121] + - contentinfo [ref=e122]: + - generic [ref=e124]: + - generic [ref=e125]: + - button "Agent" [ref=e126] [cursor=pointer] + - button "Chat" [ref=e127] [cursor=pointer] + - textbox "Message... (type @ to mention)" [active] [ref=e128] + - button "↑" [ref=e129] [cursor=pointer] + - button "Scroll to bottom": + - img + - generic: + - generic: + - button "View" + - contentinfo [ref=e130]: + - img [ref=e134] [cursor=pointer] + - generic [ref=e136]: + - combobox [ref=e138] [cursor=pointer]: + - option "🎨 Default" + - option "☀️ Light" [selected] + - option "🍊 Orange" + - option "🌃 Cyberpunk" + - option "🌴 Retrowave" + - option "💭 Vapor Dream" + - option "✨ Y2K" + - option "🔲 3D Bevel" + - option "🕹️ Arcade" + - option "🪩 Disco" + - option "🎸 Grunge" + - option "🎺 Jazz" + - option "🌻 Mellow" + - option "🏠 Mid Century" + - option "📷 Polaroid" + - option "📺 Cartoons" + - option "🏖️ Seaside" + - option "⌨️ Typewriter" + - option "📠 Xerox" + - option "📁 XTree" + - button "Sign In" [ref=e139] [cursor=pointer] + - generic [ref=e140]: + - generic [ref=e141]: 00:00 + - generic [ref=e142]: 01/01/2026 \ No newline at end of file diff --git a/.playwright-mcp/page-2026-03-31T19-19-05-563Z.yml b/.playwright-mcp/page-2026-03-31T19-19-05-563Z.yml new file mode 100644 index 0000000..5c215d1 --- /dev/null +++ b/.playwright-mcp/page-2026-03-31T19-19-05-563Z.yml @@ -0,0 +1,50 @@ +- generic [ref=e106]: + - generic [ref=e107]: + - generic [ref=e108]: Chat + - generic [ref=e109]: + - button [ref=e110] [cursor=pointer]: + - img [ref=e111] + - button [ref=e112] [cursor=pointer]: + - img [ref=e113] + - button [ref=e115] [cursor=pointer]: + - img [ref=e116] + - generic [ref=e119]: + - generic [ref=e120]: + - main [ref=e121]: + - paragraph [ref=e145]: Olá! Sou o Assistente Virtual da SEAD — Secretaria de Estado da Administração de Sergipe. Estou aqui para ajudá-lo com contracheques, agendamentos no CEAC, perícias médicas, documentos funcionais, cursos da Escola de Governo e muito mais. Como posso te ajudar hoje? + - generic [ref=e169]: 🔍 Detectar Desvios na Folha + - contentinfo [ref=e122]: + - generic [ref=e123]: + - button "📄 Segunda Via de Contracheque" [ref=e146] [cursor=pointer] + - button "📅 Agendar Atendimento no CEAC" [ref=e147] [cursor=pointer] + - button "🩺 Agendar Perícia Médica" [ref=e148] [cursor=pointer] + - button "📋 Solicitar Documentos Funcionais" [ref=e149] [cursor=pointer] + - button "✏️ Atualizar Dados Cadastrais" [ref=e150] [cursor=pointer] + - button "🎓 Cursos da Escola de Governo" [ref=e151] [cursor=pointer] + - button "💰 Informações sobre Benefícios" [ref=e152] [cursor=pointer] + - button "🔍 Consultar Protocolo" [ref=e153] [cursor=pointer] + - button "📞 Falar com a SEAD" [ref=e154] [cursor=pointer] + - button "🔍 Detectar Desvios na Folha" [active] [ref=e155] [cursor=pointer] + - button "❓ Outras Solicitações" [ref=e156] [cursor=pointer] + - button "📄 Segunda Via de Contracheque" [ref=e157] [cursor=pointer] + - button "📅 Agendar Atendimento no CEAC" [ref=e158] [cursor=pointer] + - button "🩺 Agendar Perícia Médica" [ref=e159] [cursor=pointer] + - button "📋 Solicitar Documentos Funcionais" [ref=e160] [cursor=pointer] + - button "✏️ Atualizar Dados Cadastrais" [ref=e161] [cursor=pointer] + - button "🎓 Cursos da Escola de Governo" [ref=e162] [cursor=pointer] + - button "💰 Informações sobre Benefícios" [ref=e163] [cursor=pointer] + - button "🔍 Consultar Protocolo" [ref=e164] [cursor=pointer] + - button "📞 Falar com a SEAD" [ref=e165] [cursor=pointer] + - button "🔍 Detectar Desvios na Folha" [ref=e166] [cursor=pointer] + - button "❓ Outras Solicitações" [ref=e167] [cursor=pointer] + - generic [ref=e124]: + - generic [ref=e125]: + - button "Agent" [ref=e126] [cursor=pointer] + - button "Chat" [ref=e127] [cursor=pointer] + - textbox "Message... (type @ to mention)" [ref=e128] + - button "↑" [ref=e129] [cursor=pointer] + - button "Scroll to bottom": + - img + - generic: + - generic: + - button "View" \ No newline at end of file diff --git a/.playwright-mcp/page-2026-03-31T19-19-18-155Z.yml b/.playwright-mcp/page-2026-03-31T19-19-18-155Z.yml new file mode 100644 index 0000000..056eba5 --- /dev/null +++ b/.playwright-mcp/page-2026-03-31T19-19-18-155Z.yml @@ -0,0 +1,123 @@ +- generic [ref=e106]: + - generic [ref=e107]: + - generic [ref=e108]: Chat + - generic [ref=e109]: + - button [ref=e110] [cursor=pointer]: + - img [ref=e111] + - button [ref=e112] [cursor=pointer]: + - img [ref=e113] + - button [ref=e115] [cursor=pointer]: + - img [ref=e116] + - generic [ref=e119]: + - generic [ref=e120]: + - main [ref=e121]: + - paragraph [ref=e145]: Olá! Sou o Assistente Virtual da SEAD — Secretaria de Estado da Administração de Sergipe. Estou aqui para ajudá-lo com contracheques, agendamentos no CEAC, perícias médicas, documentos funcionais, cursos da Escola de Governo e muito mais. Como posso te ajudar hoje? + - generic [ref=e169]: 🔍 Detectar Desvios na Folha + - generic [ref=e171]: + - paragraph [ref=e172]: + - text: 🕵️‍♀️ + - strong [ref=e173]: Passo a passo para detectar desvios na sua folha de pagamento + - list [ref=e174]: + - listitem [ref=e175]: + - paragraph [ref=e176]: + - strong [ref=e177]: Acesse o Contracheque Online + - list [ref=e178]: + - listitem [ref=e179]: Entre no portal SIPES/SEAD com seu login. + - listitem [ref=e180]: Visualize o contracheque do mês corrente e dos últimos 6 meses. + - listitem [ref=e181]: + - paragraph [ref=e182]: + - strong [ref=e183]: Compare os valores + - list [ref=e184]: + - listitem [ref=e185]: + - text: 📈 + - strong [ref=e186]: Remuneração Base + - text: ": deve permanecer constante (exceto progressões ou promoções)." + - listitem [ref=e187]: + - text: ➕ + - strong [ref=e188]: Verbas Variáveis + - text: ": identifique adicionais, horas extras ou gratificações que não reconhece." + - listitem [ref=e189]: + - text: ➖ + - strong [ref=e190]: Descontos + - text: ": cheque descontos de INSS, IRRF, pensão alimentícia e demais deduções." + - listitem [ref=e191]: + - paragraph [ref=e192]: + - strong [ref=e193]: Solicite documentos complementares (se necessário) + - list [ref=e194]: + - listitem [ref=e195]: + - strong [ref=e196]: Histórico de contracheques + - text: – tipo + - code [ref=e197]: CONTRACHEQUE_HISTORICO + - text: . + - listitem [ref=e198]: + - strong [ref=e199]: Informe de rendimentos + - text: ou + - strong [ref=e200]: Ficha financeira + - text: – para cruzar com a Receita Federal. + - listitem [ref=e201]: + - paragraph [ref=e202]: + - strong [ref=e203]: Cruzamento de informações + - list [ref=e204]: + - listitem [ref=e205]: + - text: Use o + - strong [ref=e206]: Informe de Rendimentos + - text: para verificar se os valores declarados coincidem com o que consta na folha. + - listitem [ref=e207]: + - text: Compare a + - strong [ref=e208]: Ficha Financeira + - text: com o extrato bancário (se houver depósito direto). + - listitem [ref=e209]: + - paragraph [ref=e210]: + - strong [ref=e211]: Registre um protocolo na SEAD/SEPLAG‑SE + - list [ref=e212]: + - listitem [ref=e213]: + - text: "Caso encontre alguma divergência, abra um protocolo (ex.:" + - code [ref=e214]: CCH123456 + - text: ). + - listitem [ref=e215]: Descreva claramente a diferença encontrada e anexe prints ou documentos. + - listitem [ref=e216]: + - paragraph [ref=e217]: + - strong [ref=e218]: Acompanhamento + - list [ref=e219]: + - listitem [ref=e220]: Acompanhe o status do protocolo pelo portal ou pelo telefone da Ouvidoria. + - listitem [ref=e221]: Se a resposta for insatisfatória, solicite revisão ou escalonamento. + - separator [ref=e222] + - paragraph [ref=e223]: + - text: ⚙️ + - strong [ref=e224]: Posso ajudar a solicitar algum desses documentos + - text: (histórico de contracheques, informe de rendimentos, ficha financeira) ou a abrir um protocolo de auditoria. Basta informar os dados solicitados (CPF, matrícula, e‑mail, etc.). + - contentinfo [ref=e122]: + - generic [ref=e123]: + - button "📄 Segunda Via de Contracheque" [ref=e225] [cursor=pointer] + - button "📅 Agendar Atendimento no CEAC" [ref=e226] [cursor=pointer] + - button "🩺 Agendar Perícia Médica" [ref=e227] [cursor=pointer] + - button "📋 Solicitar Documentos Funcionais" [ref=e228] [cursor=pointer] + - button "✏️ Atualizar Dados Cadastrais" [ref=e229] [cursor=pointer] + - button "🎓 Cursos da Escola de Governo" [ref=e230] [cursor=pointer] + - button "💰 Informações sobre Benefícios" [ref=e231] [cursor=pointer] + - button "🔍 Consultar Protocolo" [ref=e232] [cursor=pointer] + - button "📞 Falar com a SEAD" [ref=e233] [cursor=pointer] + - button "🔍 Detectar Desvios na Folha" [ref=e234] [cursor=pointer] + - button "❓ Outras Solicitações" [ref=e235] [cursor=pointer] + - button "📄 Segunda Via de Contracheque" [ref=e236] [cursor=pointer] + - button "📅 Agendar Atendimento no CEAC" [ref=e237] [cursor=pointer] + - button "🩺 Agendar Perícia Médica" [ref=e238] [cursor=pointer] + - button "📋 Solicitar Documentos Funcionais" [ref=e239] [cursor=pointer] + - button "✏️ Atualizar Dados Cadastrais" [ref=e240] [cursor=pointer] + - button "🎓 Cursos da Escola de Governo" [ref=e241] [cursor=pointer] + - button "💰 Informações sobre Benefícios" [ref=e242] [cursor=pointer] + - button "🔍 Consultar Protocolo" [ref=e243] [cursor=pointer] + - button "📞 Falar com a SEAD" [ref=e244] [cursor=pointer] + - button "🔍 Detectar Desvios na Folha" [ref=e245] [cursor=pointer] + - button "❓ Outras Solicitações" [ref=e246] [cursor=pointer] + - generic [ref=e124]: + - generic [ref=e125]: + - button "Agent" [ref=e126] [cursor=pointer] + - button "Chat" [ref=e127] [cursor=pointer] + - textbox "Message... (type @ to mention)" [ref=e128] + - button "↑" [ref=e129] [cursor=pointer] + - button "Scroll to bottom": + - img + - generic: + - generic: + - button "View" \ No newline at end of file diff --git a/.playwright-mcp/page-2026-03-31T19-21-55-814Z.yml b/.playwright-mcp/page-2026-03-31T19-21-55-814Z.yml new file mode 100644 index 0000000..a9803f8 --- /dev/null +++ b/.playwright-mcp/page-2026-03-31T19-21-55-814Z.yml @@ -0,0 +1,111 @@ +- generic [ref=e2]: + - complementary [ref=e3]: + - generic "Home" [ref=e4] [cursor=pointer]: + - img [ref=e5] + - generic "Search" [ref=e8] [cursor=pointer]: + - img [ref=e9] + - generic "Terminal" [ref=e12] [cursor=pointer]: + - img [ref=e13] + - generic "User" [ref=e15] [cursor=pointer]: + - img [ref=e16] + - generic "Apps" [ref=e19] [cursor=pointer]: + - img [ref=e20] + - generic "Settings" [ref=e25] [cursor=pointer]: + - img [ref=e26] + - generic [ref=e29]: + - generic [ref=e30]: + - generic [ref=e31]: + - generic [ref=e32] [cursor=pointer]: + - img [ref=e34] + - generic [ref=e36]: Vibe + - generic [ref=e37] [cursor=pointer]: + - img [ref=e39] + - generic [ref=e44]: CRM + - generic [ref=e45] [cursor=pointer]: + - img [ref=e47] + - generic [ref=e49]: Campaigns + - generic [ref=e50] [cursor=pointer]: + - img [ref=e52] + - generic [ref=e53]: Lists + - generic [ref=e54] [cursor=pointer]: + - img [ref=e56] + - generic [ref=e58]: Templates + - generic [ref=e59] [cursor=pointer]: + - img [ref=e61] + - generic [ref=e64]: Tasks + - generic [ref=e65] [cursor=pointer]: + - img [ref=e67] + - generic [ref=e69]: Chat + - generic [ref=e70] [cursor=pointer]: + - img [ref=e72] + - generic [ref=e74]: Terminal + - generic [ref=e75] [cursor=pointer]: + - img [ref=e77] + - generic [ref=e79]: Explorer + - generic [ref=e80] [cursor=pointer]: + - img [ref=e82] + - generic [ref=e85]: Editor + - generic [ref=e86] [cursor=pointer]: + - img [ref=e88] + - generic [ref=e93]: Designer + - generic [ref=e94] [cursor=pointer]: + - img [ref=e96] + - generic [ref=e99]: BASIC + - generic [ref=e100] [cursor=pointer]: + - img [ref=e102] + - generic [ref=e105]: Browser + - generic [ref=e106]: + - generic [ref=e107]: + - generic [ref=e108]: Chat + - generic [ref=e109]: + - button [ref=e110] [cursor=pointer]: + - img [ref=e111] + - button [ref=e112] [cursor=pointer]: + - img [ref=e113] + - button [ref=e115] [cursor=pointer]: + - img [ref=e116] + - generic [ref=e119]: + - generic [ref=e120]: + - generic: + - generic: Connecting... + - main [ref=e121] + - contentinfo [ref=e122]: + - generic [ref=e124]: + - generic [ref=e125]: + - button "Agent" [ref=e126] [cursor=pointer] + - button "Chat" [ref=e127] [cursor=pointer] + - textbox "Message... (type @ to mention)" [active] [ref=e128] + - button "↑" [ref=e129] [cursor=pointer] + - button "Scroll to bottom": + - img + - generic: + - generic: + - button "View" + - contentinfo [ref=e130]: + - img [ref=e134] [cursor=pointer] + - generic [ref=e136]: + - combobox [ref=e138] [cursor=pointer]: + - option "🎨 Default" + - option "☀️ Light" [selected] + - option "🍊 Orange" + - option "🌃 Cyberpunk" + - option "🌴 Retrowave" + - option "💭 Vapor Dream" + - option "✨ Y2K" + - option "🔲 3D Bevel" + - option "🕹️ Arcade" + - option "🪩 Disco" + - option "🎸 Grunge" + - option "🎺 Jazz" + - option "🌻 Mellow" + - option "🏠 Mid Century" + - option "📷 Polaroid" + - option "📺 Cartoons" + - option "🏖️ Seaside" + - option "⌨️ Typewriter" + - option "📠 Xerox" + - option "📁 XTree" + - button "Sign In" [ref=e139] [cursor=pointer] + - generic [ref=e140]: + - generic [ref=e141]: 00:00 + - generic [ref=e142]: 01/01/2026 \ No newline at end of file diff --git a/.playwright-mcp/page-2026-03-31T19-22-10-960Z.yml b/.playwright-mcp/page-2026-03-31T19-22-10-960Z.yml new file mode 100644 index 0000000..423c196 --- /dev/null +++ b/.playwright-mcp/page-2026-03-31T19-22-10-960Z.yml @@ -0,0 +1,61 @@ +- generic [ref=e106]: + - generic [ref=e107]: + - generic [ref=e108]: Chat + - generic [ref=e109]: + - button [ref=e110] [cursor=pointer]: + - img [ref=e111] + - button [ref=e112] [cursor=pointer]: + - img [ref=e113] + - button [ref=e115] [cursor=pointer]: + - img [ref=e116] + - generic [ref=e119]: + - generic [ref=e120]: + - main [ref=e121]: + - paragraph [ref=e145]: Olá! Sou o Assistente Virtual da SEAD — Secretaria de Estado da Administração de Sergipe. Estou aqui para ajudá-lo com contracheques, agendamentos no CEAC, perícias médicas, documentos funcionais, cursos da Escola de Governo e muito mais. Como posso te ajudar hoje? + - generic [ref=e180]: 🔍 Detectar Desvios na Folha + - contentinfo [ref=e122]: + - generic [ref=e123]: + - button "📄 Segunda Via de Contracheque" [ref=e146] [cursor=pointer] + - button "📅 Agendar Atendimento no CEAC" [ref=e147] [cursor=pointer] + - button "🩺 Agendar Perícia Médica" [ref=e148] [cursor=pointer] + - button "📋 Solicitar Documentos Funcionais" [ref=e149] [cursor=pointer] + - button "✏️ Atualizar Dados Cadastrais" [ref=e150] [cursor=pointer] + - button "🎓 Cursos da Escola de Governo" [ref=e151] [cursor=pointer] + - button "💰 Informações sobre Benefícios" [ref=e152] [cursor=pointer] + - button "🔍 Consultar Protocolo" [ref=e153] [cursor=pointer] + - button "📞 Falar com a SEAD" [ref=e154] [cursor=pointer] + - button "🔍 Detectar Desvios na Folha" [active] [ref=e155] [cursor=pointer] + - button "❓ Outras Solicitações" [ref=e156] [cursor=pointer] + - button "📄 Segunda Via de Contracheque" [ref=e157] [cursor=pointer] + - button "📅 Agendar Atendimento no CEAC" [ref=e158] [cursor=pointer] + - button "🩺 Agendar Perícia Médica" [ref=e159] [cursor=pointer] + - button "📋 Solicitar Documentos Funcionais" [ref=e160] [cursor=pointer] + - button "✏️ Atualizar Dados Cadastrais" [ref=e161] [cursor=pointer] + - button "🎓 Cursos da Escola de Governo" [ref=e162] [cursor=pointer] + - button "💰 Informações sobre Benefícios" [ref=e163] [cursor=pointer] + - button "🔍 Consultar Protocolo" [ref=e164] [cursor=pointer] + - button "📞 Falar com a SEAD" [ref=e165] [cursor=pointer] + - button "🔍 Detectar Desvios na Folha" [ref=e166] [cursor=pointer] + - button "❓ Outras Solicitações" [ref=e167] [cursor=pointer] + - button "📄 Segunda Via de Contracheque" [ref=e168] [cursor=pointer] + - button "📅 Agendar Atendimento no CEAC" [ref=e169] [cursor=pointer] + - button "🩺 Agendar Perícia Médica" [ref=e170] [cursor=pointer] + - button "📋 Solicitar Documentos Funcionais" [ref=e171] [cursor=pointer] + - button "✏️ Atualizar Dados Cadastrais" [ref=e172] [cursor=pointer] + - button "🎓 Cursos da Escola de Governo" [ref=e173] [cursor=pointer] + - button "💰 Informações sobre Benefícios" [ref=e174] [cursor=pointer] + - button "🔍 Consultar Protocolo" [ref=e175] [cursor=pointer] + - button "📞 Falar com a SEAD" [ref=e176] [cursor=pointer] + - button "🔍 Detectar Desvios na Folha" [ref=e177] [cursor=pointer] + - button "❓ Outras Solicitações" [ref=e178] [cursor=pointer] + - generic [ref=e124]: + - generic [ref=e125]: + - button "Agent" [ref=e126] [cursor=pointer] + - button "Chat" [ref=e127] [cursor=pointer] + - textbox "Message... (type @ to mention)" [ref=e128] + - button "↑" [ref=e129] [cursor=pointer] + - button "Scroll to bottom": + - img + - generic: + - generic: + - button "View" \ No newline at end of file diff --git a/.playwright-mcp/page-2026-03-31T19-22-29-106Z.yml b/.playwright-mcp/page-2026-03-31T19-22-29-106Z.yml new file mode 100644 index 0000000..dd83b4e --- /dev/null +++ b/.playwright-mcp/page-2026-03-31T19-22-29-106Z.yml @@ -0,0 +1,82 @@ +- generic [ref=e106]: + - generic [ref=e107]: + - generic [ref=e108]: Chat + - generic [ref=e109]: + - button [ref=e110] [cursor=pointer]: + - img [ref=e111] + - button [ref=e112] [cursor=pointer]: + - img [ref=e113] + - button [ref=e115] [cursor=pointer]: + - img [ref=e116] + - generic [ref=e119]: + - generic [ref=e120]: + - main [ref=e121]: + - paragraph [ref=e145]: Olá! Sou o Assistente Virtual da SEAD — Secretaria de Estado da Administração de Sergipe. Estou aqui para ajudá-lo com contracheques, agendamentos no CEAC, perícias médicas, documentos funcionais, cursos da Escola de Governo e muito mais. Como posso te ajudar hoje? + - generic [ref=e180]: 🔍 Detectar Desvios na Folha + - generic [ref=e182]: + - paragraph [ref=e183]: + - text: 🔎 + - strong [ref=e184]: Vamos analisar a sua folha de pagamento para identificar possíveis desvios. + - text: Para isso preciso que você me informe os dados cadastrais do servidor (mesmo que já os tenha usado antes). Assim consigo solicitar o histórico de contracheques e compará‑los. + - blockquote [ref=e185]: + - paragraph [ref=e186]: + - strong [ref=e187]: Dados necessários + - list [ref=e188]: + - listitem [ref=e189]: CPF (com ou sem formatação) + - listitem [ref=e190]: Matrícula funcional + - listitem [ref=e191]: Nome completo do servidor + - listitem [ref=e192]: "Órgão de lotação (ex.: SEAD, SEED, SES, SSP)" + - listitem [ref=e193]: E‑mail (para envio do documento) + - listitem [ref=e194]: Telefone com DDD + - paragraph [ref=e195]: + - text: ⚠️ + - strong [ref=e196]: Confirme + - text: que está de acordo em fornecer essas informações e eu solicito o documento + - code [ref=e197]: CONTRACHEQUE_HISTORICO + - text: . Depois analisaremos juntos as variações encontradas. + - contentinfo [ref=e122]: + - generic [ref=e123]: + - button "📄 Segunda Via de Contracheque" [ref=e198] [cursor=pointer] + - button "📅 Agendar Atendimento no CEAC" [ref=e199] [cursor=pointer] + - button "🩺 Agendar Perícia Médica" [ref=e200] [cursor=pointer] + - button "📋 Solicitar Documentos Funcionais" [ref=e201] [cursor=pointer] + - button "✏️ Atualizar Dados Cadastrais" [ref=e202] [cursor=pointer] + - button "🎓 Cursos da Escola de Governo" [ref=e203] [cursor=pointer] + - button "💰 Informações sobre Benefícios" [ref=e204] [cursor=pointer] + - button "🔍 Consultar Protocolo" [ref=e205] [cursor=pointer] + - button "📞 Falar com a SEAD" [ref=e206] [cursor=pointer] + - button "🔍 Detectar Desvios na Folha" [ref=e207] [cursor=pointer] + - button "❓ Outras Solicitações" [ref=e208] [cursor=pointer] + - button "📄 Segunda Via de Contracheque" [ref=e209] [cursor=pointer] + - button "📅 Agendar Atendimento no CEAC" [ref=e210] [cursor=pointer] + - button "🩺 Agendar Perícia Médica" [ref=e211] [cursor=pointer] + - button "📋 Solicitar Documentos Funcionais" [ref=e212] [cursor=pointer] + - button "✏️ Atualizar Dados Cadastrais" [ref=e213] [cursor=pointer] + - button "🎓 Cursos da Escola de Governo" [ref=e214] [cursor=pointer] + - button "💰 Informações sobre Benefícios" [ref=e215] [cursor=pointer] + - button "🔍 Consultar Protocolo" [ref=e216] [cursor=pointer] + - button "📞 Falar com a SEAD" [ref=e217] [cursor=pointer] + - button "🔍 Detectar Desvios na Folha" [ref=e218] [cursor=pointer] + - button "❓ Outras Solicitações" [ref=e219] [cursor=pointer] + - button "📄 Segunda Via de Contracheque" [ref=e220] [cursor=pointer] + - button "📅 Agendar Atendimento no CEAC" [ref=e221] [cursor=pointer] + - button "🩺 Agendar Perícia Médica" [ref=e222] [cursor=pointer] + - button "📋 Solicitar Documentos Funcionais" [ref=e223] [cursor=pointer] + - button "✏️ Atualizar Dados Cadastrais" [ref=e224] [cursor=pointer] + - button "🎓 Cursos da Escola de Governo" [ref=e225] [cursor=pointer] + - button "💰 Informações sobre Benefícios" [ref=e226] [cursor=pointer] + - button "🔍 Consultar Protocolo" [ref=e227] [cursor=pointer] + - button "📞 Falar com a SEAD" [ref=e228] [cursor=pointer] + - button "🔍 Detectar Desvios na Folha" [ref=e229] [cursor=pointer] + - button "❓ Outras Solicitações" [ref=e230] [cursor=pointer] + - generic [ref=e124]: + - generic [ref=e125]: + - button "Agent" [ref=e126] [cursor=pointer] + - button "Chat" [ref=e127] [cursor=pointer] + - textbox "Message... (type @ to mention)" [ref=e128] + - button "↑" [ref=e129] [cursor=pointer] + - button "Scroll to bottom": + - img + - generic: + - generic: + - button "View" \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-02T11-04-19-343Z.yml b/.playwright-mcp/page-2026-04-02T11-04-19-343Z.yml new file mode 100644 index 0000000..cbbe70c --- /dev/null +++ b/.playwright-mcp/page-2026-04-02T11-04-19-343Z.yml @@ -0,0 +1,27 @@ +- generic [ref=e5]: + - generic [ref=e6]: + - generic [ref=e7]: Chat + - generic [ref=e8]: + - button [ref=e9] [cursor=pointer]: + - img [ref=e10] + - button [ref=e11] [cursor=pointer]: + - img [ref=e12] + - button [ref=e14] [cursor=pointer]: + - img [ref=e15] + - generic [ref=e18]: + - generic [ref=e19]: + - generic: + - generic: Connecting... + - main [ref=e20] + - contentinfo [ref=e21]: + - generic [ref=e23]: + - generic [ref=e24]: + - button "Agent" [ref=e25] [cursor=pointer] + - button "Chat" [ref=e26] [cursor=pointer] + - textbox "Message... (type @ to mention)" [active] [ref=e27] + - button "↑" [ref=e28] [cursor=pointer] + - button "Scroll to bottom": + - img + - generic: + - generic: + - button "View" \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-02T11-05-27-254Z.yml b/.playwright-mcp/page-2026-04-02T11-05-27-254Z.yml new file mode 100644 index 0000000..f197640 --- /dev/null +++ b/.playwright-mcp/page-2026-04-02T11-05-27-254Z.yml @@ -0,0 +1,147 @@ +- generic [ref=e2]: + - complementary [ref=e3]: + - generic "Home" [ref=e4] [cursor=pointer]: + - img [ref=e5] + - generic "Search" [ref=e8] [cursor=pointer]: + - img [ref=e9] + - generic "Terminal" [ref=e12] [cursor=pointer]: + - img [ref=e13] + - generic "User" [ref=e15] [cursor=pointer]: + - img [ref=e16] + - generic "Apps" [ref=e19] [cursor=pointer]: + - img [ref=e20] + - generic "Settings" [ref=e25] [cursor=pointer]: + - img [ref=e26] + - generic [ref=e29]: + - generic [ref=e30]: + - generic [ref=e31]: + - generic [ref=e32] [cursor=pointer]: + - img [ref=e34] + - generic [ref=e36]: Vibe + - generic [ref=e37] [cursor=pointer]: + - img [ref=e39] + - generic [ref=e44]: CRM + - generic [ref=e45] [cursor=pointer]: + - img [ref=e47] + - generic [ref=e49]: Campaigns + - generic [ref=e50] [cursor=pointer]: + - img [ref=e52] + - generic [ref=e53]: Lists + - generic [ref=e54] [cursor=pointer]: + - img [ref=e56] + - generic [ref=e58]: Templates + - generic [ref=e59] [cursor=pointer]: + - img [ref=e61] + - generic [ref=e64]: Tasks + - generic [ref=e65] [cursor=pointer]: + - img [ref=e67] + - generic [ref=e69]: Chat + - generic [ref=e70] [cursor=pointer]: + - img [ref=e72] + - generic [ref=e74]: Terminal + - generic [ref=e75] [cursor=pointer]: + - img [ref=e77] + - generic [ref=e79]: Explorer + - generic [ref=e80] [cursor=pointer]: + - img [ref=e82] + - generic [ref=e85]: Editor + - generic [ref=e86] [cursor=pointer]: + - img [ref=e88] + - generic [ref=e93]: Designer + - generic [ref=e94] [cursor=pointer]: + - img [ref=e96] + - generic [ref=e99]: BASIC + - generic [ref=e100] [cursor=pointer]: + - img [ref=e102] + - generic [ref=e105]: Browser + - generic [ref=e106]: + - generic [ref=e107]: + - generic [ref=e108]: Chat + - generic [ref=e109]: + - button [ref=e110] [cursor=pointer]: + - img [ref=e111] + - button [ref=e112] [cursor=pointer]: + - img [ref=e113] + - button [ref=e115] [cursor=pointer]: + - img [ref=e116] + - generic [ref=e119]: + - generic [ref=e120]: + - complementary [ref=e121]: + - button "Chat" [ref=e122]: + - img [ref=e123] + - button "Tasks" [ref=e125]: + - img [ref=e126] + - button "Terminal" [ref=e129]: + - img [ref=e130] + - button "Explorer" [ref=e132]: + - img [ref=e133] + - button "Editor" [ref=e135]: + - img [ref=e136] + - button "Browser" [ref=e139]: + - img [ref=e140] + - main + - generic [ref=e143]: + - generic [ref=e144]: + - text: // BROWSER + - textbox "No preview active" [ref=e145] + - generic [ref=e147]: Waiting for app preview... + - generic [ref=e149]: // TERMINAL + - generic [ref=e150]: + - generic [ref=e151]: "Agent #1" + - text: EVOLVED + - generic [ref=e152]: Claude Opus 4.5 — 99% + - generic [ref=e153]: + - generic [ref=e154]: + - text: Plan + - button [ref=e155] + - generic [ref=e156]: + - text: YOLO + - button [ref=e157] + - generic [ref=e158]: + - generic [ref=e159]: + - button "◄" [ref=e160] + - text: 0 / 0 + - button "►" [ref=e161] + - generic [ref=e162]: + - button "💬" [ref=e163] + - button "✏️" [ref=e164] + - button "" [ref=e165] + - contentinfo [ref=e166]: + - generic [ref=e168]: Reference Entity + - generic [ref=e169]: + - generic [ref=e170]: + - button "Agent" [ref=e171] + - button "Chat" [ref=e172] + - textbox "Message... (type @ to mention)" [active] [ref=e173] + - button "↑" [ref=e174] + - button "Scroll to bottom" [ref=e175]: + - img [ref=e176] + - button "View" [ref=e180] + - contentinfo [ref=e181]: + - img [ref=e185] [cursor=pointer] + - generic [ref=e187]: + - combobox [ref=e189] [cursor=pointer]: + - option "🎨 Default" + - option "☀️ Light" + - option "🍊 Orange" + - option "🌃 Cyberpunk" + - option "🌴 Retrowave" + - option "💭 Vapor Dream" + - option "✨ Y2K" + - option "🔲 3D Bevel" + - option "🕹️ Arcade" + - option "🪩 Disco" + - option "🎸 Grunge" + - option "🎺 Jazz" + - option "🌻 Mellow" + - option "🏠 Mid Century" + - option "📷 Polaroid" + - option "📺 Cartoons" + - option "🏖️ Seaside" + - option "⌨️ Typewriter" [selected] + - option "📠 Xerox" + - option "📁 XTree" + - button "Sign In" [ref=e190] [cursor=pointer] + - generic [ref=e191]: + - generic [ref=e192]: 00:00 + - generic [ref=e193]: 01/01/2026 \ No newline at end of file diff --git a/.playwright-mcp/suggestions-container-check.json b/.playwright-mcp/suggestions-container-check.json new file mode 100644 index 0000000..f63228e --- /dev/null +++ b/.playwright-mcp/suggestions-container-check.json @@ -0,0 +1,16 @@ +{ + "found": true, + "tag": "DIV", + "className": "suggestions-container", + "innerHTML": "", + "childCount": 0, + "display": "flex", + "visibility": "visible", + "opacity": "1", + "height": "4px", + "maxHeight": "100px", + "overflow": "auto", + "hasClass": false, + "parent": "FOOTER ", + "styleAttr": null +} \ No newline at end of file diff --git a/.playwright-mcp/suggestions-css-rules.json b/.playwright-mcp/suggestions-css-rules.json new file mode 100644 index 0000000..7d91360 --- /dev/null +++ b/.playwright-mcp/suggestions-css-rules.json @@ -0,0 +1,64 @@ +{ + "rules": [ + { + "sheet": "https://chat.pragmatismo.com.br/suite/css/app.css", + "selector": "*", + "display": "", + "maxHeight": "", + "overflow": "", + "opacity": "", + "visibility": "", + "height": "", + "padding": "0px", + "margin": "0px" + }, + { + "sheet": "https://chat.pragmatismo.com.br/suite/css/app.css", + "selector": "*", + "display": "", + "maxHeight": "", + "overflow": "", + "opacity": "", + "visibility": "", + "height": "", + "padding": "", + "margin": "" + }, + { + "sheet": "https://chat.pragmatismo.com.br/suite/css/base.css", + "selector": "*", + "display": "", + "maxHeight": "", + "overflow": "", + "opacity": "", + "visibility": "", + "height": "", + "padding": "0px", + "margin": "0px" + }, + { + "sheet": "inline", + "selector": "*", + "display": "", + "maxHeight": "", + "overflow": "", + "opacity": "", + "visibility": "", + "height": "", + "padding": "0px", + "margin": "0px" + }, + { + "sheet": "https://chat.pragmatismo.com.br/suite/chat/chat.css?v=3", + "selector": ".suggestions-container", + "display": "flex", + "maxHeight": "100px", + "overflow": "", + "opacity": "", + "visibility": "", + "height": "", + "padding": "", + "margin": "" + } + ] +} \ No newline at end of file diff --git a/.playwright-mcp/suggestions-dom-check.json b/.playwright-mcp/suggestions-dom-check.json new file mode 100644 index 0000000..297c28f --- /dev/null +++ b/.playwright-mcp/suggestions-dom-check.json @@ -0,0 +1,112 @@ +[ + { + "tag": "DIV", + "className": "suggestions-container has-bot-suggestions", + "id": "suggestions", + "text": "Agendar BatizadoAgendar CasamentoAgendar MissaAgendar PeregrinaçãoPedido de OraçãoUso de ImagemLicen", + "display": "flex", + "visibility": "visible", + "hidden": false, + "parent": "FOOTER." + }, + { + "tag": "BUTTON", + "className": "suggestion-chip", + "id": "", + "text": "Agendar Batizado", + "display": "flex", + "visibility": "visible", + "hidden": false, + "parent": "DIV.suggestions-container has-bot-suggestions" + }, + { + "tag": "BUTTON", + "className": "suggestion-chip", + "id": "", + "text": "Agendar Casamento", + "display": "flex", + "visibility": "visible", + "hidden": false, + "parent": "DIV.suggestions-container has-bot-suggestions" + }, + { + "tag": "BUTTON", + "className": "suggestion-chip", + "id": "", + "text": "Agendar Missa", + "display": "flex", + "visibility": "visible", + "hidden": false, + "parent": "DIV.suggestions-container has-bot-suggestions" + }, + { + "tag": "BUTTON", + "className": "suggestion-chip", + "id": "", + "text": "Agendar Peregrinação", + "display": "flex", + "visibility": "visible", + "hidden": false, + "parent": "DIV.suggestions-container has-bot-suggestions" + }, + { + "tag": "BUTTON", + "className": "suggestion-chip", + "id": "", + "text": "Pedido de Oração", + "display": "flex", + "visibility": "visible", + "hidden": false, + "parent": "DIV.suggestions-container has-bot-suggestions" + }, + { + "tag": "BUTTON", + "className": "suggestion-chip", + "id": "", + "text": "Uso de Imagem", + "display": "flex", + "visibility": "visible", + "hidden": false, + "parent": "DIV.suggestions-container has-bot-suggestions" + }, + { + "tag": "BUTTON", + "className": "suggestion-chip", + "id": "", + "text": "Licenciamento", + "display": "flex", + "visibility": "visible", + "hidden": false, + "parent": "DIV.suggestions-container has-bot-suggestions" + }, + { + "tag": "BUTTON", + "className": "suggestion-chip", + "id": "", + "text": "Evento/Iluminação", + "display": "flex", + "visibility": "visible", + "hidden": false, + "parent": "DIV.suggestions-container has-bot-suggestions" + }, + { + "tag": "BUTTON", + "className": "suggestion-chip", + "id": "", + "text": "Cadastrar Guia", + "display": "flex", + "visibility": "visible", + "hidden": false, + "parent": "DIV.suggestions-container has-bot-suggestions" + }, + { + "tag": "BUTTON", + "className": "suggestion-chip", + "id": "", + "text": "Fazer Doação", + "display": "flex", + "visibility": "visible", + "hidden": false, + "parent": "DIV.suggestions-container has-bot-suggestions" + } +] \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index 2f347cc..d4399a8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -246,6 +246,7 @@ match x { ## ❌ Absolute Prohibitions - NEVER search /target folder! It is binary compiled. +- ❌ **NEVER** hardcode passwords, tokens, API keys, or any credentials in source code — ALWAYS use `generate_random_string()` or environment variables - ❌ **NEVER** build in release mode - ONLY debug builds allowed - ❌ **NEVER** use `--release` flag on ANY cargo command - ❌ **NEVER** run `cargo build` - use `cargo check` for syntax verification @@ -860,3 +861,70 @@ Continue on gb/ workspace. Follow AGENTS.md strictly: - **VERIFY LAST** - Only compile/diagnostics after ALL fixes - **DELETE DEAD CODE** - Don't keep unused code around - **GIT WORKFLOW** - ALWAYS push to ALL repositories (github, pragmatismo) + +--- + +## Deploy in Prod Workflow + +### CI/CD Pipeline (Primary Method) + +1. **Push to ALM** — triggers CI/CD automatically: + ```bash + cd botserver + git push alm main + git push origin main + cd .. + git add botserver + git commit -m "Update botserver: " + git push alm main + git push origin main + ``` + +2. **Wait for CI** — build takes ~3-4 minutes. Check status: + ```bash + # Via web: https://alm.pragmatismo.com.br/GeneralBots/botserver/actions + # Or check binary timestamp after ~4 min sleep: + sleep 240 + ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 administrator@prod-host \ + "sudo incus exec system -- stat -c '%y' /opt/gbo/bin/botserver" + ``` + +3. **Restart in prod** — after binary updates: + ```bash + ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 administrator@prod-host \ + "sudo incus exec system -- pkill -f botserver || true" + sleep 2 + ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 administrator@prod-host \ + "sudo incus exec system -- bash -c 'cd /opt/gbo/bin && sudo -u gbuser RUST_LOG=info ./botserver --noconsole > /opt/gbo/logs/botserver-output.log 2>&1 &'" + ``` + +4. **Verify deployment**: + ```bash + # Wait for bootstrap (~2 min) + sleep 120 + # Check health + ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 administrator@prod-host \ + "sudo incus exec system -- curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/health" + # Check logs + ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 administrator@prod-host \ + "sudo incus exec system -- tail -30 /opt/gbo/logs/botserver-output.log" + ``` + +### Troubleshooting + +| Symptom | Cause | Fix | +|---------|-------|-----| +| `valkey-cli ping` hangs indefinitely | Valkey requires password auth | Install `nc` or `ss` in container for health checks | +| `nc: command not found` | Prod container lacks netcat | `sudo incus exec system -- apt-get install -y netcat-openbsd` | +| Cache connection timeout | iptables DROP rule on port 6379 | `sudo incus exec system -- iptables -I INPUT -i lo -j ACCEPT` | +| `AUTH failed` on Valkey | Valkey runs without password but Vault has one | Code tries no-password URL first, then with password | +| `Cannot start a runtime from within a runtime` | `block_on()` called from async context | Use `.await` directly, never `runtime.block_on()` in async functions | +| Secret not found in Vault | Path mismatch between seeding and reading | Seeding: `secret/gbo/cache`, Reading: `gbo/cache` (kv2 prepends `secret/`) | +| CI completed but binary not updated | Deploy step fails silently (SSH/transfer issue) | Build locally and transfer, or re-push to trigger CI again | + +### Critical Paths in Vault + +- **Seeding writes to**: `secret/gbo/{service}` (e.g., `secret/gbo/cache`) +- **Code reads via**: `SecretPaths::{SERVICE}` which maps to `gbo/{service}` +- **kv2::read** prepends `secret/` automatically and looks up `secret/data/gbo/{service}` +- **All paths must match**: `gbo/cache`, `gbo/drive`, `gbo/tables`, `gbo/directory`, `gbo/llm`, `gbo/meet`, `gbo/alm`, `gbo/vectordb`, `gbo/encryption`, `gbo/email` diff --git a/Cargo.lock b/Cargo.lock index 14ffca5..faeb9bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1275,7 +1275,7 @@ dependencies = [ [[package]] name = "botapp" -version = "6.3.0" +version = "6.1.0" dependencies = [ "anyhow", "botlib", diff --git a/Cargo.toml b/Cargo.toml index c591623..f27ff45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "bottest", "botui", ] +exclude = ["backup-to-s3"] [workspace.lints.rust] diff --git a/backup-to-s3/.env.example b/backup-to-s3/.env.example new file mode 100644 index 0000000..a90d8ad --- /dev/null +++ b/backup-to-s3/.env.example @@ -0,0 +1,7 @@ +AWS_ACCESS_KEY_ID=your_access_key +AWS_SECRET_ACCESS_KEY=your_secret_key +AWS_REGION=us-east-1 +S3_BUCKET=prod-gbo-backup +INCUS_PROJECT=PROD-GBO1 +CONTAINER_NAME= +KEEP_COUNT=10 diff --git a/backup-to-s3/Cargo.lock b/backup-to-s3/Cargo.lock new file mode 100644 index 0000000..128beda --- /dev/null +++ b/backup-to-s3/Cargo.lock @@ -0,0 +1,1818 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "backup-to-s3" +version = "0.1.0" +dependencies = [ + "chrono", + "dotenv", + "rusoto_core", + "rusoto_credential", + "rusoto_s3", + "tokio", + "tracing", + "tracing-appender", + "tracing-subscriber", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cc" +version = "1.2.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac", + "digest", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.10", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4c90f45aa2e6eacbe8645f77fdea542ac97a494bcd117a67df9ff4d611f995" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.183" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" + +[[package]] +name = "libredox" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08" +dependencies = [ + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "md-5" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" +dependencies = [ + "block-buffer", + "digest", + "opaque-debug", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-conv" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl" +version = "0.10.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "openssl-sys" +version = "0.9.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rusoto_core" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1db30db44ea73551326269adcf7a2169428a054f14faf9e1768f2163494f2fa2" +dependencies = [ + "async-trait", + "base64", + "bytes", + "crc32fast", + "futures", + "http", + "hyper", + "hyper-tls", + "lazy_static", + "log", + "rusoto_credential", + "rusoto_signature", + "rustc_version", + "serde", + "serde_json", + "tokio", + "xml-rs", +] + +[[package]] +name = "rusoto_credential" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee0a6c13db5aad6047b6a44ef023dbbc21a056b6dab5be3b79ce4283d5c02d05" +dependencies = [ + "async-trait", + "chrono", + "dirs-next", + "futures", + "hyper", + "serde", + "serde_json", + "shlex", + "tokio", + "zeroize", +] + +[[package]] +name = "rusoto_s3" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aae4677183411f6b0b412d66194ef5403293917d66e70ab118f07cc24c5b14d" +dependencies = [ + "async-trait", + "bytes", + "futures", + "rusoto_core", + "xml-rs", +] + +[[package]] +name = "rusoto_signature" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ae95491c8b4847931e291b151127eccd6ff8ca13f33603eb3d0035ecb05272" +dependencies = [ + "base64", + "bytes", + "chrono", + "digest", + "futures", + "hex", + "hmac", + "http", + "hyper", + "log", + "md-5", + "percent-encoding", + "pin-project-lite", + "rusoto_credential", + "rustc_version", + "serde", + "sha2", + "tokio", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tokio" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.3", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-appender" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" +dependencies = [ + "crossbeam-channel", + "thiserror 2.0.18", + "time", + "tracing-subscriber", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6523d69017b7633e396a89c5efab138161ed5aafcbc8d3e5c5a42ae38f50495a" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e3a6c758eb2f701ed3d052ff5737f5bfe6614326ea7f3bbac7156192dc32e67" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921de2737904886b52bcbb237301552d05969a6f9c40d261eb0533c8b055fedf" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a93e946af942b58934c604527337bad9ae33ba1d5c6900bbb41c2c07c2364a93" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "xml-rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/backup-to-s3/Cargo.toml b/backup-to-s3/Cargo.toml new file mode 100644 index 0000000..30b5114 --- /dev/null +++ b/backup-to-s3/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "backup-to-s3" +version = "0.1.0" +edition = "2021" + +[dependencies] +tokio = { version = "1", features = ["full"] } +rusoto_s3 = "0.48" +rusoto_core = "0.48" +rusoto_credential = "0.48" +dotenv = "0.15" +chrono = "0.4" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +tracing-appender = "0.2" diff --git a/backup-to-s3/src/main.rs b/backup-to-s3/src/main.rs new file mode 100644 index 0000000..46d6540 --- /dev/null +++ b/backup-to-s3/src/main.rs @@ -0,0 +1,213 @@ +use chrono::Utc; +use rusoto_core::Region; +use rusoto_credential::StaticProvider; +use rusoto_s3::{S3, S3Client}; +use std::env; +use tracing::{error, info, warn}; + +#[derive(Debug)] +struct Config { + access_key: String, + secret_key: String, + bucket: String, + project: String, + keep_count: usize, + region: Region, +} + +impl Config { + fn from_env() -> Result { + dotenv::from_path(".env").ok(); + + let access_key = env::var("AWS_ACCESS_KEY_ID").map_err(|_| "AWS_ACCESS_KEY_ID not set")?; + let secret_key = env::var("AWS_SECRET_ACCESS_KEY").map_err(|_| "AWS_SECRET_ACCESS_KEY not set")?; + let bucket = env::var("S3_BUCKET").map_err(|_| "S3_BUCKET not set")?; + let project = env::var("INCUS_PROJECT").unwrap_or_else(|_| "PROD-GBO1".to_string()); + let keep_count: usize = env::var("KEEP_COUNT").unwrap_or_else(|_| "10".to_string()).parse().unwrap_or(10); + let region_name = env::var("AWS_REGION").unwrap_or_else(|_| "us-east-1".to_string()); + let region = region_name.parse().unwrap_or(Region::UsEast1); + + Ok(Config { + access_key, + secret_key, + bucket, + project, + keep_count, + region, + }) + } +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + tracing_subscriber::fmt() + .with_env_filter("info") + .init(); + + let config = Config::from_env()?; + info!("Starting backup to S3 bucket: {}", config.bucket); + + let credential_provider = StaticProvider::new_minimal( + config.access_key.clone(), + config.secret_key.clone(), + ); + let s3_client = S3Client::new_with( + rusoto_core::HttpClient::new().map_err(|e| format!("HTTP client: {}", e))?, + credential_provider, + config.region.clone(), + ); + + let timestamp = Utc::now().format("%Y-%m-%d").to_string(); + let container_name = env::var("CONTAINER_NAME").unwrap_or_default(); + + let containers = if container_name.is_empty() { + list_inc_containers(&config.project).await? + } else { + vec![container_name] + }; + + for container in containers { + info!("Backing up container: {}", container); + + match backup_container(&container, &config, &s3_client, ×tamp).await { + Ok(_) => info!("Successfully backed up {}", container), + Err(e) => { + error!("Failed to backup {}: {}", container, e); + continue; + } + } + } + + cleanup_old_backups(&config, &s3_client).await?; + + info!("Backup completed!"); + Ok(()) +} + +async fn list_inc_containers(project: &str) -> Result, String> { + let output = tokio::process::Command::new("incus") + .args(["list", "--project", project, "--format", "csv", "-c", "n"]) + .output() + .await + .map_err(|e| format!("Failed to list containers: {}", e))?; + + if !output.status.success() { + return Err("incus list failed".to_string()); + } + + let stdout = String::from_utf8_lossy(&output.stdout); + let containers: Vec = stdout.lines() + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + .collect(); + + Ok(containers) +} + +async fn backup_container( + container: &str, + config: &Config, + s3_client: &S3Client, + timestamp: &str, +) -> Result<(), String> { + let temp_dir = std::env::temp_dir(); + let backup_file = temp_dir.join(format!("{}.tar.gz", container)); + + info!("Exporting {} to {:?}", container, backup_file); + + let export_output = tokio::process::Command::new("incus") + .args([ + "export", + container, + backup_file.to_str().unwrap(), + "--instance-only", + "--project", + &config.project, + ]) + .output() + .await + .map_err(|e| format!("Failed to export container: {}", e))?; + + if !export_output.status.success() { + let stderr = String::from_utf8_lossy(&export_output.stderr); + return Err(format!("incus export failed: {}", stderr)); + } + + let s3_key = format!("{}/{}.tar.gz", timestamp, container); + info!("Uploading to s3://{}/{}", config.bucket, s3_key); + + let file_data = tokio::fs::read(&backup_file) + .await + .map_err(|e| format!("Failed to read backup file: {}", e))?; + + let put_request = rusoto_s3::PutObjectRequest { + body: Some(file_data.into()), + bucket: config.bucket.clone(), + key: s3_key, + ..Default::default() + }; + + s3_client.put_object(put_request) + .await + .map_err(|e| format!("Failed to upload to S3: {}", e))?; + + tokio::fs::remove_file(&backup_file) + .await + .map_err(|e| warn!("Failed to delete temp file: {}", e)).ok(); + + info!("Uploaded {}", container); + Ok(()) +} + +async fn cleanup_old_backups(config: &Config, s3_client: &S3Client) -> Result<(), String> { + info!("Cleaning up old backups, keeping {} most recent", config.keep_count); + + let list_output = s3_client + .list_objects_v2(rusoto_s3::ListObjectsV2Request { + bucket: config.bucket.clone(), + delimiter: Some("/".to_string()), + ..Default::default() + }) + .await + .map_err(|e| format!("Failed to list S3: {}", e))?; + + let mut prefixes: Vec = list_output + .common_prefixes + .unwrap_or_default() + .iter() + .map(|p| p.prefix.clone().unwrap_or_default().trim_end_matches('/').to_string()) + .filter(|p| !p.is_empty()) + .collect(); + + prefixes.sort_by(|a, b| b.cmp(a)); + + if prefixes.len() > config.keep_count { + for prefix in prefixes.iter().skip(config.keep_count) { + info!("Deleting old backup: {}", prefix); + + let objects = s3_client + .list_objects_v2(rusoto_s3::ListObjectsV2Request { + bucket: config.bucket.clone(), + prefix: Some(format!("{}/", prefix)), + ..Default::default() + }) + .await + .map_err(|e| format!("Failed to list objects: {}", e))?; + + for obj in objects.contents.unwrap_or_default() { + if let Some(key) = obj.key { + s3_client + .delete_object(rusoto_s3::DeleteObjectRequest { + bucket: config.bucket.clone(), + key, + ..Default::default() + }) + .await + .ok(); + } + } + } + } + + Ok(()) +} diff --git a/botserver b/botserver index dae0feb..7b4753a 160000 --- a/botserver +++ b/botserver @@ -1 +1 @@ -Subproject commit dae0feb6a567dd1b7e66a382786deef4e7f1fb8a +Subproject commit 7b4753af0d1c8b2ce763e5171676e8116ddd5fb4 diff --git a/default-vault.tar b/default-vault.tar deleted file mode 100644 index e69de29..0000000 diff --git a/migrations.tar.gz b/migrations.tar.gz deleted file mode 100644 index 3cb6e48..0000000 Binary files a/migrations.tar.gz and /dev/null differ diff --git a/ping_google.cmd b/ping_google.cmd new file mode 100644 index 0000000..772744c --- /dev/null +++ b/ping_google.cmd @@ -0,0 +1,5 @@ +@echo off +:loop +ping google.com +timeout /t 18000 /nobreak >nul +goto loop diff --git a/prompts/c1.md b/prompts/c1.md index 9db67d1..dd356ac 100644 --- a/prompts/c1.md +++ b/prompts/c1.md @@ -1,85 +1,29 @@ -# Plan: Migrate LXC Containers to Incus (COMPLETED) +Migrate 10 LXC containers from source (root@82.29.59.188 - LXD/ZFS) to destination (administrator@63.141.255.9 - Incus/Btrfs) using streaming method. +Instructions +- Use Btrfs storage on destination (loop1 mounted at /mnt/btrfs) +- Use streaming method: tar | zstd | ssh (no local storage) +- Migrate one by one: stop → copy → create → start → delete +- DO NOT delete containers before copying data +Discoveries +- Source: LXD with ZFS backend (default/containers/*), containers at /var/snap/lxd/common/lxd/storage-pools/default/containers/ +- Destination: Incus 6.23, Btrfs pool PROD-GBO on loop1, project PROD-GBO1 +- SSH: Works from source root → destination administrator +- MISTAKE MADE: Deleted all Incus containers with cleanup command before data was properly inside them +Accomplished +- ✅ Created Btrfs storage pool PROD-GBO on destination +- ✅ SSH access configured from source to destination +- ❌ Containers were deleted during cleanup - they need to be recreated +- ❌ Data is outside containers, needs to be copied INTO containers properly -## Summary -✅ All containers migrated from LXD (pragmatismo.com.br) to Incus (63.141.255.9) -✅ All data synced from host /opt/gbo/tenants/ to containers -✅ All binaries copied from source to containers -✅ All services configured and running +Next steps +1. Create empty Incus containers: incus create --empty --project PROD-GBO1 -s PROD-GBO +2. Migrate from source: tar -C rootfs -cf - . | zstd | ssh dest 'sudo tar -I zstd -xf - -C /mnt/btrfs/containers/PROD-GBO1_/rootfs' +3. Start containers: incus start +4. Delete from source: lxc delete --force -## Container & Service Status -| Container | Service | Status | -|-----------|---------|--------| -| dns | coredns | ✅ RUNNING | -| email | stalwart-mail | ✅ RUNNING | -| webmail | php built-in server (:5252) | ✅ RUNNING | -| alm | forgejo | ✅ RUNNING | -| drive | minio | ✅ RUNNING | -| tables | postgresql | ✅ RUNNING | -| system | botserver | ✅ RUNNING | - - -## Service Files Location -All service files in `/etc/systemd/system/` inside containers: -- `dns.service` - coredns (User=root) -- `email.service` - stalwart-mail (User=root) -- `alm.service` - forgejo (User=alm, Group=alm) -- `minio.service` - minio (User=root) - -## Binary Locations -| Service | Binary Path | -|---------|-------------| -| coredns | /opt/gbo/bin/coredns | -| stalwart | /opt/gbo/bin/stalwart | -| forgejo | /opt/gbo/bin/forgejo | -| minio | /usr/local/bin/minio | - -## Key Paths Inside Containers -- **Binaries**: /opt/gbo/bin/ -- **Data**: /opt/gbo/data/ -- **Config**: /opt/gbo/conf/ -- **Logs**: /opt/gbo/logs/ - -## IPS (Destination) -- dns: 10.107.115.155 -- email: 10.107.115.200 -- webmail: 10.107.115.208 -- alm: 10.107.115.4 -- drive: 10.107.115.114 -- tables: 10.107.115.33 -- system: 10.107.115.229 - -- alm-ci: 10.107.115.190 -- table-editor: 10.107.115.73 - -## Port Forwarding (iptables NAT) -``` -# DNS -sudo iptables -t nat -A PREROUTING -p tcp --dport 53 -j DNAT --to-destination 10.107.115.155:53 -sudo iptables -t nat -A PREROUTING -p udp --dport 53 -j DNAT --to-destination 10.107.115.155:53 - -# Email -sudo iptables -t nat -A PREROUTING -p tcp --dport 25 -j DNAT --to-destination 10.107.115.200:25 -sudo iptables -t nat -A PREROUTING -p tcp --dport 587 -j DNAT --to-destination 10.107.115.200:587 -sudo iptables -t nat -A PREROUTING -p tcp --dport 465 -j DNAT --to-destination 10.107.115.200:465 -sudo iptables -t nat -A PREROUTING -p tcp --dport 143 -j DNAT --to-destination 10.107.115.200:143 -sudo iptables -t nat -A PREROUTING -p tcp --dport 993 -j DNAT --to-destination 10.107.115.200:993 -sudo iptables -t nat -A PREROUTING -p tcp --dport 110 -j DNAT --to-destination 10.107.115.200:110 -sudo iptables -t nat -A PREROUTING -p tcp --dport 995 -j DNAT --to-destination 10.107.115.200:995 -sudo iptables -t nat -A PREROUTING -p tcp --dport 4190 -j DNAT --to-destination 10.107.115.200:4190 - -# Webmail -sudo iptables -t nat -A PREROUTING -p tcp --dport 5252 -j DNAT --to-destination 10.107.115.208:5252 - -# ALM (forgejo) -sudo iptables -t nat -A PREROUTING -p tcp --dport 4747 -j DNAT --to-destination 10.107.115.4:4747 - -# Caddy (80, 443) - already exists for proxy container -``` - -## Workflow (PRODUCTION TESTED) -1. Copy container: `incus copy --instance-only lxd-source: ` -2. Add eth0 network: `incus config device add eth0 nic name=eth0 network=PROD-GBO` -3. Sync data: `incus file push --recursive /opt/gbo/tenants/pragmatismo// /opt/gbo/` -4. Copy binaries: from source via `lxc file pull` → scp to dest → `incus file push` -5. Create service file: `cat > /tmp/.service && incus file push .service /etc/systemd/system/` -6. Enable/start: `incus exec -- systemctl enable && systemctl start ` +Accomplished +- Recreated Btrfs storage pool PROD-GBO on /mnt/btrfs +- Created 10 empty Incus containers +- Migrated all containers using tar | zstd | ssh +- All 10 containers running on Btrfs +- Used ~24GB of Btrfs storage diff --git a/prompts/c3.md b/prompts/c3.md new file mode 100644 index 0000000..f03743d --- /dev/null +++ b/prompts/c3.md @@ -0,0 +1,461 @@ +# Fast Migration Plan - LXC/LXD to Incus (ONE BY ONE) + +## Strategy: STREAMING DATA TRANSFER (NO DISK I/O ON SOURCE) + +Transfer container data directly via compressed stream, NO intermediate tarballs on source. +Use `pv` (pipe viewer) for colorful progress bars and rate monitoring. + +--- + +## Why This is FASTEST + +✅ **NO disk writes on source** (99% full) - streaming only +✅ **zstd compression** - 3-5x faster than gzip +✅ **pv monitoring** - colorful progress, ETA, rate +✅ **Parallel transfers** - migrate multiple containers simultaneously +✅ **Immediate deletion** - frees space for next transfer + +--- + +## Prerequisites + +### On Source (82.29.59.188) +```bash +# Install required tools +sudo apt update && sudo apt install -y zstd pv + +# List all containers +lxc list +``` + +### On Destination (63.141.255.9) +```bash +# Incus is already installed and clean (LXC/LXD removed) + +# Create PROD-GBO network bridge +sudo incus network create PROD-GBO --type bridge ipv4.address=10.107.115.1/24 + +# Create PROD-GBO1 project +sudo incus project create PROD-GBO1 +sudo incus project switch PROD-GBO1 + +# Configure project +sudo incus project set PROD-GBO1 features.storage.volumes=true +sudo incus project set PROD-GBO1 features.profiles=true +``` + +--- + +## Migration Workflow (Per Container) + +### Step 1: Stop Container on Source +```bash +# On source server +lxc stop pragmatismo-alm +``` + +### Step 2: Stream Data to Destination (FASTEST - NO DISK I/O) +```bash +# On source server - stream compressed data directly +lxc exec pragmatismo-alm -- tar -cf - /opt/gbo/ | \ + zstd -3 -q | \ + pv -s $(du -sb /opt/gbo | awk '{print $1}') | \ + ssh administrator@63.141.255.9 "zstd -d | tar -xf - -C ~/gbo/pragmatismo-alm/" +``` + +**What happens:** +1. `tar` reads /opt/gbo from container (streamed, no disk write) +2. `zstd -3` compresses on-the-fly (fast compression) +3. `pv` shows colorful progress with ETA and rate +4. `ssh` streams to destination +5. Destination decompresses and extracts directly to ~/gbo/pragmatismo-alm/ +6. **NO tarball on source disk** - solves 99% full problem! + +**pv Options:** +- `-s` : Total size (from `du -sb` for accurate progress) +- Colorful output with ETA, rate, elapsed time + +### Step 3: Delete Container from Source +```bash +# Free space immediately for next transfer +lxc delete pragmatismo-alm +``` + +### Step 4: Create Fresh Incus Container +```bash +# On destination server +sudo incus launch images:debian/12/cloud pragmatismo-alm + +# Add network +sudo incus config device add pragmatismo-alm eth0 nic name=eth0 network=PROD-GBO + +# Set static IP +sudo incus config set pragmatismo-alm ipv4.address=10.107.115.4 + +# Create gbuser inside container +sudo incus exec pragmatismo-alm -- useradd -m -s /bin/bash gbuser +``` + +### Step 5: Push Data to Container +```bash +# Create directory structure +sudo incus exec pragmatismo-alm -- mkdir -p /opt/gbo + +# Push data (recursive) +sudo incus file push -r ~/gbo/pragmatismo-alm/* pragmatismo-alm/opt/gbo/ + +# Fix ownership +sudo incus exec pragmatismo-alm -- chown -R gbuser:gbuser /opt/gbo + +# Make binaries executable +sudo incus exec pragmatismo-alm -- chmod +x /opt/gbo/bin/* +``` + +### Step 6: Start Container and Verify Service +```bash +sudo incus start pragmatismo-alm + +# Check service status +sudo incus exec pragmatismo-alm -- systemctl status alm + +# Check logs +sudo incus exec pragmatismo-alm -- journalctl -u alm -f +``` + +### Step 7: Configure NAT Rules (ON HOST, NOT IN CONTAINER) +```bash +# On destination host (63.141.255.9) + +# For ALM (example): +sudo iptables -t nat -A PREROUTING -p tcp --dport 4747 -j DNAT --to-destination 10.107.115.4:4747 +sudo iptables -t nat -A OUTPUT -p tcp --dport 4747 -j DNAT --to-destination 10.107.115.4:4747 +sudo iptables -A FORWARD -p tcp -d 10.107.115.4 --dport 4747 -j ACCEPT +sudo iptables -t nat -A POSTROUTING -p tcp -d 10.107.115.4 -j MASQUERADE + +# Save rules +sudo sh -c 'iptables-save > /etc/iptables/rules.v4' +``` + +--- + +## Full Migration Script (ALL CONTAINERS) + +### Run Multiple Containers in Parallel (for speed) + +```bash +#!/bin/bash +# MIGRATE-ALL.sh - Fast parallel migration + +set -e + +# Container list with IPs +declare -A CONTAINERS=( + ["dns"]="10.107.115.155" + ["email"]="10.107.115.200" + ["webmail"]="10.107.115.208" + ["alm"]="10.107.115.4" + ["drive"]="10.107.115.114" + ["tables"]="10.107.115.33" + ["system"]="10.107.115.229" + ["alm-ci"]="10.107.115.190" + ["table-editor"]="10.107.115.73" +) + +for container in "${!CONTAINERS[@]}"; do + ip="${CONTAINERS[$container]}" + + echo -e "\e[1;32m=== Migrating $container ($ip) ===\e[0m" + + # Step 1: Stop + echo -e "\e[1;33mStopping $container...\e[0m" + ssh root@82.29.59.188 "lxc stop $container" || true + + # Step 2: Get size for pv + echo -e "\e[1;33mGetting size of /opt/gbo...\e[0m" + size=$(ssh root@82.29.59.188 "lxc exec $container -- du -sb /opt/gbo | awk '{print \$1}'") + + # Step 3: Stream data (FAST!) + echo -e "\e[1;33mStreaming data to destination...\e[0m" + ssh root@82.29.59.188 "lxc exec $container -- tar -cf - /opt/gbo/" | \ + zstd -3 -q | \ + pv -s $size | \ + ssh administrator@63.141.255.9 "mkdir -p ~/gbo/$container && zstd -d | tar -xf - -C ~/gbo/$container/" + + # Step 4: Delete from source + echo -e "\e[1;33mDeleting $container from source...\e[0m" + ssh root@82.29.59.188 "lxc delete $container" + + # Step 5: Create fresh container + echo -e "\e[1;33mCreating Incus container...\e[0m" + ssh administrator@63.141.255.9 "sudo incus launch images:debian/12/cloud $container && \ + sudo incus config device add $container eth0 nic name=eth0 network=PROD-GBO && \ + sudo incus config set $container ipv4.address=$ip && \ + sudo incus exec $container -- useradd -m -s /bin/bash gbuser" + + # Step 6: Push data + echo -e "\e[1;33mPushing data to container...\e[0m" + ssh administrator@63.141.255.9 "sudo incus exec $container -- mkdir -p /opt/gbo && \ + sudo incus file push -r ~/gbo/$container/* $container:/opt/gbo/ && \ + sudo incus exec $container -- chown -R gbuser:gbuser /opt/gbo && \ + sudo incus exec $container -- chmod +x /opt/gbo/bin/*" + + # Step 7: Start + echo -e "\e[1;33mStarting $container...\e[0m" + ssh administrator@63.141.255.9 "sudo incus start $container" + + echo -e "\e[1;32m✓ $container migrated successfully!\e[0m" + echo "" +done + +echo -e "\e[1;32m=== ALL MIGRATIONS COMPLETE ===\e[0m" +``` + +--- + +## Single Container Migration (Quick Test) + +```bash +#!/bin/bash +# migrate-one.sh - Migrate single container + +CONTAINER=$1 + +if [ -z "$CONTAINER" ]; then + echo "Usage: $0 " + exit 1 +fi + +echo -e "\e[1;32m=== Migrating $CONTAINER ===\e[0m" + +# Stop on source +echo -e "\e[1;33m[1/7] Stopping container...\e[0m" +ssh root@82.29.59.188 "lxc stop $CONTAINER" + +# Get size +echo -e "\e[1;33m[2/7] Getting data size...\e[0m" +SIZE=$(ssh root@82.29.59.188 "lxc exec $CONTAINER -- du -sb /opt/gbo | awk '{print \$1}'") + +# Stream data (NO DISK WRITE!) +echo -e "\e[1;33m[3/7] Streaming data (pv will show progress)...\e[0m" +ssh root@82.29.59.188 "lxc exec $CONTAINER -- tar -cf - /opt/gbo/" | \ + zstd -3 -q | \ + pv -s $SIZE | \ + ssh administrator@63.141.255.9 "mkdir -p ~/gbo/$CONTAINER && zstd -d | tar -xf - -C ~/gbo/$CONTAINER/" + +# Delete from source +echo -e "\e[1;33m[4/7] Deleting from source...\e[0m" +ssh root@82.29.59.188 "lxc delete $CONTAINER" + +# Create container +echo -e "\e[1;33m[5/7] Creating Incus container...\e[0m" +ssh administrator@63.141.255.9 "sudo incus launch images:debian/12/cloud $CONTAINER && \ + sudo incus config device add $CONTAINER eth0 nic name=eth0 network=PROD-GBO && \ + sudo incus exec $CONTAINER -- useradd -m -s /bin/bash gbuser" + +# Push data +echo -e "\e[1;33m[6/7] Pushing data...\e[0m" +ssh administrator@63.141.255.9 "sudo incus exec $CONTAINER -- mkdir -p /opt/gbo && \ + sudo incus file push -r ~/gbo/$CONTAINER/* $CONTAINER:/opt/gbo/ && \ + sudo incus exec $CONTAINER -- chown -R gbuser:gbuser /opt/gbo && \ + sudo incus exec $CONTAINER -- chmod +x /opt/gbo/bin/*" + +# Start +echo -e "\e[1;33m[7/7] Starting container...\e[0m" +ssh administrator@63.141.255.9 "sudo incus start $CONTAINER" + +echo -e "\e[1;32m✓ Migration complete! Check with: incus list\e[0m" +``` + +--- + +## Port Forwarding (iptables NAT) - Complete Rules + +```bash +# Enable IP forwarding (persistent) +echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/99-ipforward.conf +sudo sysctl -w net.ipv4.ip_forward=1 + +# Enable route_localnet +echo "net.ipv4.conf.all.route_localnet = 1" | sudo tee /etc/sysctl.d/99-localnet.conf +sudo sysctl -w net.ipv4.conf.all.route_localnet=1 + +# DNS (53) +sudo iptables -t nat -A PREROUTING -p udp --dport 53 -j DNAT --to-destination 10.107.115.155:53 +sudo iptables -t nat -A PREROUTING -p tcp --dport 53 -j DNAT --to-destination 10.107.115.155:53 +sudo iptables -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to-destination 10.107.115.155:53 +sudo iptables -t nat -A OUTPUT -p tcp --dport 53 -j DNAT --to-destination 10.107.115.155:53 + +# Email (25,465,587,993,995,143,110,4190) +sudo iptables -t nat -A PREROUTING -p tcp --dport 25 -j DNAT --to-destination 10.107.115.200:25 +sudo iptables -t nat -A PREROUTING -p tcp --dport 465 -j DNAT --to-destination 10.107.115.200:465 +sudo iptables -t nat -A PREROUTING -p tcp --dport 587 -j DNAT --to-destination 10.107.115.200:587 +sudo iptables -t nat -A PREROUTING -p tcp --dport 993 -j DNAT --to-destination 10.107.115.200:993 +sudo iptables -t nat -A PREROUTING -p tcp --dport 995 -j DNAT --to-destination 10.107.115.200:995 +sudo iptables -t nat -A PREROUTING -p tcp --dport 143 -j DNAT --to-destination 10.107.115.200:143 +sudo iptables -t nat -A PREROUTING -p tcp --dport 110 -j DNAT --to-destination 10.107.115.200:110 +sudo iptables -t nat -A PREROUTING -p tcp --dport 4190 -j DNAT --to-destination 10.107.115.200:4190 +sudo iptables -t nat -A OUTPUT -p tcp --dport 25 -j DNAT --to-destination 10.107.115.200:25 +sudo iptables -t nat -A OUTPUT -p tcp --dport 465 -j DNAT --to-destination 10.107.115.200:465 +sudo iptables -t nat -A OUTPUT -p tcp --dport 587 -j DNAT --to-destination 10.107.115.200:587 +sudo iptables -t nat -A OUTPUT -p tcp --dport 993 -j DNAT --to-destination 10.107.115.200:993 +sudo iptables -t nat -A OUTPUT -p tcp --dport 995 -j DNAT --to-destination 10.107.115.200:995 +sudo iptables -t nat -A OUTPUT -p tcp --dport 143 -j DNAT --to-destination 10.107.115.200:143 +sudo iptables -t nat -A OUTPUT -p tcp --dport 110 -j DNAT --to-destination 10.107.115.200:110 +sudo iptables -t nat -A OUTPUT -p tcp --dport 4190 -j DNAT --to-destination 10.107.115.200:4190 + +# Webmail (5252) +sudo iptables -t nat -A PREROUTING -p tcp --dport 5252 -j DNAT --to-destination 10.107.115.208:5252 +sudo iptables -t nat -A OUTPUT -p tcp --dport 5252 -j DNAT --to-destination 10.107.115.208:5252 + +# ALM (4747) +sudo iptables -t nat -A PREROUTING -p tcp --dport 4747 -j DNAT --to-destination 10.107.115.4:4747 +sudo iptables -t nat -A OUTPUT -p tcp --dport 4747 -j DNAT --to-destination 10.107.115.4:4747 + +# Tables/PostgreSQL (5432) +sudo iptables -t nat -A PREROUTING -p tcp --dport 5432 -j DNAT --to-destination 10.107.115.33:5432 +sudo iptables -t nat -A OUTPUT -p tcp --dport 5432 -j DNAT --to-destination 10.107.115.33:5432 + +# Proxy/Caddy (80, 443) +sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.107.115.236:80 +sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j DNAT --to-destination 10.107.115.236:443 +sudo iptables -t nat -A OUTPUT -p tcp --dport 80 -j DNAT --to-destination 10.107.115.236:80 +sudo iptables -t nat -A OUTPUT -p tcp --dport 443 -j DNAT --to-destination 10.107.115.236:443 + +# FORWARD rules (allow traffic to containers) +sudo iptables -A FORWARD -p tcp -d 10.107.115.155 -j ACCEPT +sudo iptables -A FORWARD -p udp -d 10.107.115.155 -j ACCEPT +sudo iptables -A FORWARD -p tcp -d 10.107.115.200 -j ACCEPT +sudo iptables -A FORWARD -p tcp -s 10.107.115.200 -j ACCEPT +sudo iptables -A FORWARD -p tcp -d 10.107.115.208 -j ACCEPT +sudo iptables -A FORWARD -p tcp -d 10.107.115.4 -j ACCEPT +sudo iptables -A FORWARD -p tcp -d 10.107.115.33 -j ACCEPT +sudo iptables -A FORWARD -p tcp -d 10.107.115.236 -j ACCEPT +sudo iptables -A FORWARD -p tcp -s 10.107.115.236 -j ACCEPT + +# POSTROUTING MASQUERADE (return traffic) +sudo iptables -t nat -A POSTROUTING -p tcp -d 10.107.115.155 -j MASQUERADE +sudo iptables -t nat -A POSTROUTING -p udp -d 10.107.115.155 -j MASQUERADE +sudo iptables -t nat -A POSTROUTING -p tcp -d 10.107.115.200 -j MASQUERADE +sudo iptables -t nat -A POSTROUTING -p tcp -d 10.107.115.208 -j MASQUERADE +sudo iptables -t nat -A POSTROUTING -p tcp -d 10.107.115.4 -j MASQUERADE +sudo iptables -t nat -A POSTROUTING -p tcp -d 10.107.115.33 -j MASQUERADE +sudo iptables -t nat -A POSTROUTING -p tcp -d 10.107.115.236 -j MASQUERADE + +# Save rules +sudo sh -c 'iptables-save > /etc/iptables/rules.v4' +``` + +--- + +## Benefits of This Approach + +✅ **FASTEST** - No intermediate tarballs, pure streaming +✅ **COLORFUL** - pv shows beautiful progress bars +✅ **EFFICIENT** - zstd compression (3-5x gzip) +✅ **SAFE** - One container at a time, verify before next +✅ **CLEAN** - Immediate deletion frees disk space +✅ **MONITORED** - Real-time transfer rate and ETA +✅ **PARALLEL** - Can migrate multiple containers at once + +**Estimated time:** +- Small containers (<1GB): ~5-10 minutes each +- Medium containers (1-5GB): ~10-20 minutes each +- Large containers (>5GB): ~20-40 minutes each +- Total (parallel): ~1-2 hours for all containers + +vs lxc export method: ~4-8 hours total + +--- + +## Quick Start + +```bash +# 1. Install pv on source +ssh root@82.29.59.188 "apt install -y pv zstd" + +# 2. Save migration scripts +# Copy migrate-one.sh and MIGRATE-ALL.sh + +# 3. Run single test +./migrate-one.sh pragmatismo-alm + +# 4. If successful, run all +./MIGRATE-ALL.sh +``` + +--- + +## Troubleshooting + +### pv not showing progress? +```bash +# Check pv is installed +pv --version + +# Check size detection +ssh root@82.29.59.188 "lxc exec pragmatismo-alm -- du -sb /opt/gbo" +``` + +### Container not starting? +```bash +# Check logs +sudo incus list +sudo incus info pragmatismo-alm +sudo incus logs pragmatismo-alm --show-log + +# Check network +sudo incus exec pragmatismo-alm -- ip addr +``` + +### Service not running? +```bash +# Inside container +sudo incus exec pragmatismo-alm -- bash +systemctl status alm +journalctl -u alm -n 50 +``` + +### NAT not working? +```bash +# Check rules +sudo iptables -t nat -L -n -v + +# Check forwarding +sudo iptables -L FORWARD -n -v + +# Test from host +nc -zv 127.0.0.1 4747 +``` + +--- + +## Summary + +This plan achieves: +✅ **FASTEST possible migration** - streaming, no disk I/O +✅ **Colorful progress** - pv with ETA, rate, elapsed +✅ **Immediate space cleanup** - delete as we go +✅ **Parallel migration** - do multiple containers at once +✅ **Clean Incus** - fresh start, no LXC/LXD trash +✅ **Proper NAT** - iptables only, no socat/proxy devices + +**No mbuffer needed** - pv does the job with colorful output! + +--- + +## Verification Checklist + +After each migration: +- [ ] Container running: `sudo incus list` +- [ ] Service active: `sudo incus exec $c -- systemctl status ` +- [ ] Data intact: `sudo incus exec $c -- ls -la /opt/gbo` +- [ ] Port accessible: `nc -zv 127.0.0.1 ` +- [ ] Source deleted: `ssh root@82.29.59.188 lxc list` (should not show container) +- [ ] NAT rules added: `sudo iptables -t nat -L -n | grep ` + +After all migrations: +- [ ] All containers running on Incus +- [ ] All services active +- [ ] All NAT rules configured +- [ ] External access works +- [ ] Source server: no containers left diff --git a/prompts/container.md b/prompts/container.md index bdea1b8..e2e6d8f 100644 --- a/prompts/container.md +++ b/prompts/container.md @@ -6,6 +6,33 @@ This document describes how to improve `installer.rs` to automate the deployment --- +# rust incus install base +sudo apt install -y curl gnupg + + +sudo mkdir -p /etc/apt/keyrings + + +sudo curl -fsSL https://pkgs.zabbly.com/key.asc -o /etc/apt/keyrings/zabbly.asc + + +sudo sh -c 'cat > /etc/apt/sources.list.d/zabbly-incus-stable.sources << EOF +Enabled: yes +Types: deb +URIs: https://pkgs.zabbly.com/incus/stable +Suites: bookworm +Components: main +Architectures: $(dpkg --print-architecture) +Signed-By: /etc/apt/keyrings/zabbly.asc +EOF' + + +sudo apt update + + +sudo apt install incus + + ## What Was Done Manually (Reference Implementation) ### Migration Summary (pragmatismo tenant) @@ -449,8 +476,6 @@ mail IN A --- -## Container Cleanup (BEFORE Setting Up NAT) - **ALWAYS remove socat and Incus proxy devices before configuring iptables NAT:** ```bash diff --git a/prompts/fail2ban-start.sh b/prompts/fail2ban-start.sh deleted file mode 100644 index 55b64bf..0000000 --- a/prompts/fail2ban-start.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -# fail2ban startup script for Incus containers -# Usage: Place in /opt/gbo/bin/ and run as root in container - -LOGFILE=/opt/gbo/logs/fail2ban.log - -mkdir -p /opt/gbo/logs -nohup /usr/bin/fail2ban-server -x -f > $LOGFILE 2>&1 & -sleep 2 -fail2ban-client reload -echo "Fail2ban started - check status with: fail2ban-client status" \ No newline at end of file diff --git a/prompts/go.md b/prompts/go.md deleted file mode 100644 index c20d520..0000000 --- a/prompts/go.md +++ /dev/null @@ -1,15 +0,0 @@ -# Production Smoke Test Plan - -## Status: Caddy running ✅ - -## URLs to Test (YOLO) -1. https://webmail.pragmatismo.com.br/ -2. https://chat.pragmatismo.com.br/cristo -3. https://alm.pragmatismo.com.br/ -4. https://tables.pragmatismo.com.br/ - -## Steps -1. Navigate to each URL -2. Take screenshot -3. Check for errors in console/network -4. Report pass/fail per URL diff --git a/prompts/migrate.md b/prompts/migrate.md deleted file mode 100644 index 713cf89..0000000 --- a/prompts/migrate.md +++ /dev/null @@ -1,18 +0,0 @@ -# LXC to Incus Migration Script - -```bash -#!/bin/bash -# Export all LXC containers and transfer to destination - -containers="pragmatismo-alm pragmatismo-alm-ci pragmatismo-dns pragmatismo-drive pragmatismo-email pragmatismo-proxy pragmatismo-system pragmatismo-table-editor pragmatismo-tables pragmatismo-webmail" - -for name in $containers; do - echo "=== Migrating $name ===" - ssh root@pragmatismo.com.br "lxc export $name /tmp/$name.tar.gz" - scp root@pragmatismo.com.br:/tmp/$name.tar.gz administrator@63.141.255.9:~/ - ssh root@pragmatismo.com.br "rm /tmp/$name.tar.gz" - echo "✓ $name transferred" -done - -echo "Migration complete. All .tar.gz files in ~/ on destination." -``` diff --git a/prompts/win.md b/prompts/win.md deleted file mode 100644 index b8ecc46..0000000 --- a/prompts/win.md +++ /dev/null @@ -1,244 +0,0 @@ -# General Bots — Plano de Execução no Windows - -## Pré-requisitos - -- Windows 10/11 (x64) -- Visual Studio Build Tools (com C++ workload) ou Visual Studio -- Rust toolchain (`rustup` com `stable-x86_64-pc-windows-msvc`) -- Git para Windows - ---- - -## 1. Dependências de Build - -### 1.1 PostgreSQL (libpq.lib para Diesel ORM) - -O BotServer usa Diesel com backend Postgres. No Windows, o linker precisa de `libpq.lib`. - -```powershell -# Baixar binários do PostgreSQL (não precisa instalar o serviço) -Invoke-WebRequest -Uri "https://get.enterprisedb.com/postgresql/postgresql-17.4-1-windows-x64-binaries.zip" -OutFile "$env:TEMP\pgsql.zip" -UseBasicParsing - -# Extrair para C:\pgsql -Expand-Archive -Path "$env:TEMP\pgsql.zip" -DestinationPath "C:\pgsql" -Force - -# Configurar variável de ambiente permanente -[System.Environment]::SetEnvironmentVariable("PQ_LIB_DIR", "C:\pgsql\pgsql\lib", "User") -$env:PQ_LIB_DIR = "C:\pgsql\pgsql\lib" -$env:PATH = "C:\pgsql\pgsql\bin;$env:PATH" -``` - -Ou simplesmente execute: -```powershell -.\DEPENDENCIES.ps1 -``` - -### 1.2 sccache (opcional) - -O `.cargo/config.toml` referencia `sccache`. Se não estiver instalado, comente a linha: -```toml -# [build] -# rustc-wrapper = "sccache" -``` - ---- - -## 2. Compilação - -```powershell -# Garantir que PQ_LIB_DIR está configurado -$env:PQ_LIB_DIR = "C:\pgsql\pgsql\lib" - -# Build do botserver -cargo build -p botserver - -# Build do botui (interface desktop Tauri) -cargo build -p botui -``` - -### Correções de compilação aplicadas - -| Arquivo | Problema | Correção | -|---------|----------|----------| -| `bootstrap.rs` | `use std::os::unix::fs::PermissionsExt` | Envolvido com `#[cfg(unix)]` | -| `antivirus.rs` | `error!` macro não importada | Adicionado `use tracing::error` | -| `installer.rs` | `check_root()` só existe no Unix | Adicionado `check_admin()` para Windows com `#[cfg(windows)]` | -| `installer.rs` | `SUDOERS_CONTENT` não usado no Windows | Envolvido com `#[cfg(not(windows))]` | -| `facade.rs` | `make_executable` param `path` não usado | Renomeado para `_path` no Windows | - ---- - -## 3. Compatibilidade de Runtime (3rdparty.toml) - -O `3rdparty.toml` agora tem entradas `_windows` para cada componente: - -| Componente | Linux | Windows | -|------------|-------|---------| -| `drive` | `minio` (linux-amd64) | `minio.exe` (windows-amd64) | -| `tables` | `postgresql-*-linux-gnu.tar.gz` | `postgresql-*-windows-x64-binaries.zip` | -| `cache` | `valkey-*-jammy-x86_64.tar.gz` | `memurai-developer.msi` | -| `llm` | `llama-*-ubuntu-x64.zip` | `llama-*-win-cpu-x64.zip` (ou CUDA/Vulkan) | -| `email` | `stalwart-mail-*-linux.tar.gz` | `stalwart-mail-*-windows.zip` | -| `proxy` | `caddy_*_linux_amd64.tar.gz` | `caddy_*_windows_amd64.zip` | -| `directory` | `zitadel-linux-amd64.tar.gz` | `zitadel-windows-amd64.zip` | -| `alm` | `forgejo-*-linux-amd64` | `forgejo-*-windows-amd64.exe` | -| `alm_ci` | `forgejo-runner-*-linux-amd64` | `forgejo-runner-*-windows-amd64.exe` | -| `dns` | `coredns_*_linux_amd64.tgz` | `coredns_*_windows_amd64.tgz` | -| `meet` | `livekit_*_linux_amd64.tar.gz` | `livekit_*_windows_amd64.zip` | -| `table_editor` | `nocodb-linux-x64` | `nocodb-win-x64.exe` | -| `vector_db` | `qdrant-*-linux-gnu.tar.gz` | `qdrant-*-windows-msvc.zip` | -| `timeseries_db` | `influxdb2-*-linux-amd64.tar.gz` | `influxdb2-*-windows-amd64.zip` | -| `vault` | `vault_*_linux_amd64.zip` | `vault_*_windows_amd64.zip` | -| `observability` | `vector-*-linux-gnu.tar.gz` | `vector-*-windows-msvc.zip` | - -A seleção é automática via `get_component_url()` em `installer.rs` que busca `{name}_windows` primeiro. - ---- - -## 4. Adaptações de Código para Windows - -### 4.1 Execução de Comandos Shell - -| Contexto | Linux | Windows | -|----------|-------|---------| -| `run_commands_with_password()` | `bash -c "{cmd}"` | `powershell -NoProfile -Command "{cmd}"` | -| `start()` (spawnar processos) | `sh -c "{exec_cmd}"` | `powershell -NoProfile -Command "{exec_cmd}"` | -| `safe_sh_command()` | `sh -c` | `powershell -NoProfile -Command` | - -### 4.2 Gerenciamento de Processos - -| Ação | Linux | Windows | -|------|-------|---------| -| Matar processos | `pkill -9 -f {name}` | `taskkill /F /IM {name}*` | -| Verificar processo | `pgrep -f {name}` | `Get-Process \| Where-Object { $_.ProcessName -like '*{name}*' }` | - -### 4.3 Health Checks (fallback quando curl não está disponível) - -| Check | Linux | Windows | -|-------|-------|---------| -| Porta aberta | `nc -z -w 1 127.0.0.1 {port}` | `(Test-NetConnection -ComputerName 127.0.0.1 -Port {port}).TcpTestSucceeded` | - -### 4.4 Extração de Arquivos - -| Formato | Linux | Windows | -|---------|-------|---------| -| `.zip` | `unzip -o -q {file}` | `Expand-Archive -Path '{file}' -DestinationPath '{dest}' -Force` | -| `.tar.gz` | `tar -xzf {file}` | `tar -xzf {file}` (Windows 10+ tem tar nativo) | - ---- - -## 5. Execução - -### 5.1 Modo CLI (gerenciar componentes) -```powershell -$env:PQ_LIB_DIR = "C:\pgsql\pgsql\lib" -$env:PATH = "C:\pgsql\pgsql\bin;$env:PATH" - -# Instalar um componente específico -.\target\debug\botserver.exe install vault - -# Listar componentes -.\target\debug\botserver.exe list - -# Iniciar componentes -.\target\debug\botserver.exe start - -# Ver status -.\target\debug\botserver.exe status -``` - -### 5.2 Modo Servidor (bootstrap completo + HTTP) -```powershell -$env:PQ_LIB_DIR = "C:\pgsql\pgsql\lib" -$env:PATH = "C:\pgsql\pgsql\bin;$env:PATH" - -# Executa bootstrap (baixa/instala todos os componentes) + inicia servidor HTTP -.\target\debug\botserver.exe -``` - -O bootstrap automático: -1. Baixa e instala Vault, PostgreSQL, Valkey, MinIO, Zitadel, LLM, etc. -2. Gera certificados TLS -3. Inicializa o banco de dados -4. Inicia o servidor HTTP na porta **5858** - -Acesse: **http://localhost:5858** - -### 5.3 Via restart.ps1 -```powershell -.\restart.ps1 -``` - ---- - -## 6. Detecção Automática de GPU (LLM) - -O sistema detecta automaticamente: -- **CUDA 13.x** → `llama-*-win-cuda-13.1-x64.zip` -- **CUDA 12.x** → `llama-*-win-cuda-12.4-x64.zip` -- **Vulkan SDK** → `llama-*-win-vulkan-x64.zip` -- **CPU only** → `llama-*-win-cpu-x64.zip` -- **ARM64** → `llama-*-win-cpu-arm64.zip` - ---- - -## 7. Estrutura de Diretórios - -``` -botserver-stack/ -├── bin/ # Binários dos componentes -│ ├── vault/ -│ ├── tables/ # PostgreSQL -│ ├── cache/ # Valkey/Memurai -│ ├── drive/ # MinIO -│ ├── directory/ # Zitadel -│ ├── llm/ # llama.cpp -│ └── ... -├── conf/ # Configurações -│ ├── vault/ -│ ├── system/certificates/ -│ └── ... -├── data/ # Dados persistentes -│ ├── vault/ -│ ├── tables/pgdata/ -│ └── ... -└── logs/ # Logs de cada componente - ├── vault/ - ├── tables/ - └── ... - -botserver-installers/ # Cache de downloads (reutilizado) -``` - ---- - -## 8. Troubleshooting - -### Erro: `LNK1181: libpq.lib não encontrado` -```powershell -$env:PQ_LIB_DIR = "C:\pgsql\pgsql\lib" -# Ou execute .\DEPENDENCIES.ps1 -``` - -### Erro: `sccache não encontrado` -Comente no `.cargo/config.toml`: -```toml -# [build] -# rustc-wrapper = "sccache" -``` - -### Erro: `Path traversal detected` -Limpe o cache e recompile: -```powershell -Remove-Item -Path ".\botserver-stack" -Recurse -Force -Remove-Item -Path ".\botserver-installers" -Recurse -Force -cargo clean -p botserver -cargo build -p botserver -``` - -### Componentes baixam versão Linux -Recompile o botserver para que o `3rdparty.toml` embutido seja atualizado: -```powershell -cargo clean -p botserver -cargo build -p botserver -``` diff --git a/prompts/workflow.md b/prompts/workflow.md deleted file mode 100644 index 2b87d70..0000000 --- a/prompts/workflow.md +++ /dev/null @@ -1,242 +0,0 @@ -# BASIC Workflow Engine Plan - -## Current State - -- `workflow_executions` and `workflow_events` tables exist in DB -- `WorkflowExecution` / `WorkflowEvent` models exist in `core/shared/models/workflow_models.rs` -- `ORCHESTRATE WORKFLOW` keyword exists in `basic/keywords/orchestration.rs` (stub) -- `STEP` keyword registered but not durable -- Compiler (`basic/mod.rs`) produces Rhai AST and runs it in one shot via `engine.eval_ast_with_scope` -- `HEAR` currently blocks a thread (new) — works but not crash-safe - ---- - -## Goal - -BASIC scripts run as **durable step sequences**. Each keyword is a step. On crash/restart, execution resumes from the last completed step. No re-run. No Rhai for control flow. - -```basic -' ticket.bas -TALK "Describe the issue" ← Step 1 -HEAR description ← Step 2 (suspends, waits) -SET ticket = CREATE(description) ← Step 3 -TALK "Ticket #{ticket} created" ← Step 4 -``` - ---- - -## Two Execution Modes - -The compiler serves both modes via a pragma at the top of the `.bas` file: - -```basic -' Default: Rhai mode (current behavior, fast, no durability) -TALK "Hello" - -' Workflow mode (durable, crash-safe) -#workflow -TALK "Hello" -HEAR name -``` - -`ScriptService::compile()` detects `#workflow` and returns either: -- `ExecutionPlan::Rhai(AST)` — current path, unchanged -- `ExecutionPlan::Workflow(Vec)` — new path - -`ScriptService::run()` dispatches accordingly. - ---- - -## Architecture - -### 1. Compiler changes (`basic/mod.rs`, `basic/compiler/`) - -Add `compile_to_steps(script: &str) -> Result>`: - -```rust -pub enum Step { - Talk { template: String }, - Hear { variable: String, input_type: String }, - Set { variable: String, expression: String }, - If { condition: String, then_steps: Vec, else_steps: Vec }, - Call { function: String, args: Vec, result_var: Option }, - // ... one variant per keyword -} -``` - -Expressions inside steps (`condition`, `expression`, `template`) are still evaluated by Rhai — but only as **pure expression evaluator**, no custom syntax, no side effects. This keeps Rhai as a math/string engine only. - -### 2. WorkflowEngine (`basic/workflow/engine.rs`) - -```rust -pub struct WorkflowEngine { - state: Arc, - session: UserSession, -} - -impl WorkflowEngine { - /// Start a new workflow or resume existing one for this session+script - pub async fn run(&self, script_path: &str, steps: Vec) -> Result<()> - - /// Execute one step, persist result, return next action - async fn execute_step(&self, exec_id: Uuid, step: &Step, vars: &mut Variables) -> StepResult - - /// Load execution state from DB - async fn load_state(&self, exec_id: Uuid) -> (usize, Variables) - - /// Persist step completion - async fn save_state(&self, exec_id: Uuid, step_index: usize, vars: &Variables, status: &str) -} - -pub enum StepResult { - Continue, // go to next step - Suspend, // HEAR — save state, return, wait for next message - Done, // script finished -} -``` - -### 3. HEAR in workflow mode - -No thread blocking. Instead: - -1. `execute_step(Hear)` saves state to `workflow_executions` with `status = "waiting"`, `current_step = N` -2. Returns `StepResult::Suspend` → engine returns to caller -3. Next user message → `stream_response` checks `workflow_executions` for `session_id` with `status = "waiting"` -4. Loads variables, sets `variables["description"] = user_input`, advances `current_step`, resumes - -### 4. `stream_response` dispatch (`core/bot/mod.rs`) - -```rust -// At top of stream_response, before LLM: -if let Some(exec) = WorkflowEngine::find_waiting(state, session_id).await { - WorkflowEngine::resume(state, exec, message_content).await?; - return Ok(()); -} -``` - -### 5. DB schema (already exists, minor additions) - -```sql --- Already exists: -workflow_executions (id, bot_id, workflow_name, current_step, state_json, status, ...) - --- Add: -ALTER TABLE workflow_executions ADD COLUMN session_id UUID; -ALTER TABLE workflow_executions ADD COLUMN script_path TEXT; --- state_json stores: { "variables": {...}, "step_index": N } -``` - ---- - -## Migration Path - -### Phase 1 — Parallel mode (no breaking changes) -- Add `compile_to_steps()` alongside existing `compile()` -- Add `WorkflowEngine` as new struct -- `#workflow` pragma routes to new path -- All existing `.bas` files unchanged, run via Rhai as before - -### Phase 2 — Keyword parity -Implement step variants for all keywords used in practice: -`TALK`, `HEAR`, `SET`, `IF/ELSE/END IF`, `CALL` (HTTP, LLM, tool), `SEND MAIL`, `SCHEDULE` - -### Phase 3 — Default for new scripts -New `.bas` files default to workflow mode. Rhai mode kept for backwards compat and tool scripts (short-lived, no HEAR). - -### Phase 4 — Rhai scope reduction -Remove Rhai custom syntax registrations. Keep Rhai only as expression evaluator: -```rust -engine.eval_expression::(&expr, &scope) -``` - ---- - -## File Map - -``` -basic/ - mod.rs ← add compile_to_steps(), ExecutionPlan enum - compiler/ - mod.rs ← existing Rhai compiler, unchanged - step_compiler.rs ← NEW: BASIC → Vec - workflow/ - mod.rs ← NEW: WorkflowEngine - engine.rs ← NEW: execute_step, load/save state - variables.rs ← NEW: Variables (HashMap) - steps.rs ← NEW: Step enum - keywords/ ← existing, unchanged in Phase 1 -``` - ---- - -## Keyword Compatibility - -### Category A — Workflow steps (implement as `Step` variants) -`TALK`, `HEAR`, `SET`, `IF/ELSE/END IF`, `SEND MAIL`, `SEND TEMPLATE`, `SCHEDULE`, -`SAVE`/`INSERT`/`UPDATE`, `GET`, `FIND`, `SEARCH`, `USE KB`, `USE TOOL`, `REMEMBER`, -`HTTP GET/POST/PUT/DELETE`, `WAIT`, `TRANSFER TO HUMAN`, `CREATE TASK`, `BOOK`, `SCORE LEAD` - -### Category B — Pure expressions (Rhai as calculator, no step boundary) -`math/*`, `datetime/*`, `string_functions`, `arrays/*`, `core_functions`, `validation/*`, `FORMAT` -→ Stored as expression strings in Step, evaluated at runtime via `engine.eval_expression_with_scope()` - -### Category C — Rhai-only (scripts using these stay in Rhai mode, no `#workflow`) -`code_sandbox`, `use_website`, `face_api`, `on_change`, `on_email`, `webhook`, -`procedures` (FUNCTION/SUB/CALL), `for_next` (FOR EACH loops), `switch_case`, `events`, `orchestration` - -A script with any Category C keyword cannot use `#workflow`. The compiler detects this and errors early. - ---- - -## How Compilation Works Without Rhai - -Workflow compiler is a **line-by-line parser**, not a Rhai AST walk: - -``` -Input line → Step variant -───────────────────────────────────────────────────── -TALK "Hello ${name}" → Step::Talk { template } -HEAR description → Step::Hear { var, input_type } -SET x = score + 1 → Step::Set { var, expr: "score + 1" } -IF score > 10 THEN → Step::If { cond: "score > 10", then_steps, else_steps } -SEND MAIL to, s, b → Step::SendMail { to, subject, body } -USE TOOL path → Step::UseTool { path, args } -``` - -Expressions (`score + 1`, `score > 10`) are stored as **raw strings** in the Step struct. -At runtime, Rhai evaluates them as pure expressions — no custom syntax, no side effects: - -```rust -let mut engine = Engine::new(); // no register_custom_syntax calls -let mut scope = Scope::new(); -for (k, v) in &variables { scope.push_dynamic(k, v.clone()); } -let result = engine.eval_expression_with_scope::(&mut scope, expr)?; -``` - -Rhai remains a dependency but is used only as a math/string expression evaluator (~5 lines of code at runtime). All custom keyword machinery is bypassed entirely. - ---- - - - -| Engine | Lang | Latency | RAM | Rust SDK | Verdict | -|--------|------|---------|-----|----------|---------| -| **Custom (this plan)** | Rust | ~1ms | 0 extra | Native | ✅ Best fit | -| **[Restate](https://restate.dev)** | Rust server | ~5ms | ~50MB | ✅ official | Fallback option | -| **[Rhythm](https://github.com/maxnorth/rhythm)** | Rust | ~2ms | ~10MB | Native | Experimental | -| **Temporal** | Go+Java | ~20ms | ~500MB | ❌ | Too heavy | -| **Windmill** | Rust+TS | ~10ms | ~200MB | ❌ | Wrong abstraction | - -**Why custom over Restate:** Restate requires its own server as a proxy between HTTP requests and handlers — adds a network hop and an extra process. The custom plan uses PostgreSQL already running in the stack, zero extra infrastructure. - -**Escape hatch:** The `Step` enum in this plan maps 1:1 to Restate workflow steps. If the custom engine proves too complex to maintain, migration to Restate is mechanical — swap `WorkflowEngine::execute_step` internals, keep the compiler and Step enum unchanged. - ---- - - - -- **No re-run ever.** Steps before current_step are skipped on resume. -- **Rhai never removed entirely** — used for expression eval only. -- **Backwards compatible** — no `#workflow` = Rhai mode, existing bots unaffected. -- **HEAR in workflow mode = zero threads held.** State in DB, not RAM. -- **Tool scripts** (called by LLM) stay in Rhai mode — they're short-lived, no HEAR needed. diff --git a/restart.sh b/restart.sh index a9f3613..60ee9d1 100755 --- a/restart.sh +++ b/restart.sh @@ -4,21 +4,38 @@ set -e echo "Stopping..." pkill -f botserver || true pkill -f botui || true +pkill -f botmodels || true pkill -f rustc || true echo "Cleaning..." -rm -f botserver.log botui.log +rm -f botserver.log botui.log botmodels.log echo "Building..." cargo build -p botserver cargo build -p botui +echo "Starting botmodels..." +cd botmodels +source venv/bin/activate +uvicorn src.main:app --host 0.0.0.0 --port 8085 > ../botmodels.log 2>&1 & +echo " PID: $!" +cd .. + +echo "Waiting for botmodels..." +for i in $(seq 1 30); do + if curl -s http://localhost:8085/api/health > /dev/null 2>&1; then + echo " botmodels ready" + break + fi + sleep 1 +done + echo "Starting botserver..." -RUST_LOG=debug ./target/debug/botserver --noconsole > botserver.log 2>&1 & +BOTMODELS_HOST="http://localhost:8085" BOTMODELS_API_KEY="starter" RUST_LOG=debug ./target/debug/botserver --noconsole > botserver.log 2>&1 & echo " PID: $!" echo "Starting botui..." BOTSERVER_URL="http://localhost:8080" ./target/debug/botui > botui.log 2>&1 & echo " PID: $!" -echo "Done. Logs: tail -f botserver.log botui.log" +echo "Done. Logs: tail -f botserver.log botui.log botmodels.log" diff --git a/usekb2.md b/usekb2.md new file mode 100644 index 0000000..027b301 --- /dev/null +++ b/usekb2.md @@ -0,0 +1,154 @@ +# USE KB 2.0: Group-Based Knowledge Base Access + +## Overview +Modify the USE KB keyword to respect user group permissions, ensuring that THINK KB queries only return answers from knowledge base folders that belong to groups the logged-in user is a member of. + +## Current Architecture + +### USE KB Flow +1. User executes `USE KB "kb_name"` in BASIC script +2. `use_kb.rs:add_kb_to_session()` checks if KB exists in `kb_collections` +3. Creates default KB entry if not found +4. Adds association to `session_kb_associations` table +5. KB becomes active for the session + +### THINK KB Flow +1. User executes `THINK KB "query"` +2. `think_kb.rs:think_kb_search()` gets all active KBs from `session_kb_associations` +3. For each active KB, calls `KnowledgeBaseManager.search()` on its Qdrant collection +4. Returns combined results from all active KBs + +### Group System +- Groups stored in `rbac_groups` table +- User membership in `rbac_user_groups` table +- Group permissions via `rbac_group_roles` table + +## Proposed Changes + +### 1. Database Schema Changes + +Add new table `kb_group_associations`: + +```sql +CREATE TABLE kb_group_associations ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + kb_id UUID NOT NULL REFERENCES kb_collections(id) ON DELETE CASCADE, + group_id UUID NOT NULL REFERENCES rbac_groups(id) ON DELETE CASCADE, + granted_by UUID REFERENCES users(id) ON DELETE SET NULL, + granted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + UNIQUE(kb_id, group_id) +); +``` + +Migration file: `botserver/migrations/6.2.0-01-kb-groups/up.sql` + +### 2. Backend Logic Changes + +#### Modify `think_kb_search()` in `think_kb.rs` +- Add user group lookup before searching +- Filter active KBs to only those accessible by user's groups +- Allow access if KB has no group associations (public KBs) OR user is in associated groups + +```rust +async fn think_kb_search( + kb_manager: Arc, + db_pool: DbPool, + session_id: Uuid, + bot_id: Uuid, + user_id: Uuid, // Add user_id parameter + query: &str, +) -> Result { + // Get user's groups + let user_groups = get_user_groups(&db_pool, user_id)?; + + // Get active KBs filtered by groups + let accessible_kbs = get_accessible_kbs_for_session(&db_pool, session_id, &user_groups)?; + + // Search only accessible KBs + // ... rest of search logic +} +``` + +#### Add `get_accessible_kbs_for_session()` function +```rust +fn get_accessible_kbs_for_session( + conn_pool: &DbPool, + session_id: Uuid, + user_groups: &[String], +) -> Result, String> { + // Query that joins session_kb_associations with kb_group_associations + // Returns KBs where group_id IS NULL (public) OR group_id IN user_groups +} +``` + +#### Modify `add_kb_to_session()` in `use_kb.rs` +- Add optional group access check +- Allow USE KB if user has access to the KB's groups + +### 3. API Changes + +Add new endpoints in `rbac.rs` for KB-group management: + +```rust +// Assign KB to group +POST /api/rbac/kbs/{kb_id}/groups/{group_id} + +// Remove KB from group +DELETE /api/rbac/kbs/{kb_id}/groups/{group_id} + +// Get groups for KB +GET /api/rbac/kbs/{kb_id}/groups + +// Get KBs accessible by user +GET /api/rbac/users/{user_id}/accessible-kbs +``` + +### 4. Frontend Changes + +#### Update `botui/ui/suite/admin/groups.html` +- Add "Knowledge Bases" tab to group detail panel +- Show list of KBs assigned to the group +- Allow adding/removing KB assignments + +#### Update `botui/ui/suite/drive/drive.html` +- Add group visibility indicators for KB folders +- Show which groups have access to each KB + +### 5. Migration Strategy + +1. Create new migration for `kb_group_associations` table +2. Run migration to create table +3. Assign existing KBs to default groups (e.g., "all_users" group) +4. Update application code +5. Deploy and test + +### 6. Backward Compatibility + +- Existing KBs without group associations remain public +- Existing USE KB calls continue to work +- THINK KB will filter results based on new permissions + +## Implementation Steps + +1. ✅ Database migration for kb_group_associations +2. ✅ Modify think_kb_search to accept user_id and filter by groups +3. ✅ Update THINK KB keyword registration to pass user_id +4. ✅ Add group access check to USE KB +5. ✅ Add API endpoints for KB-group management +6. ✅ Update admin UI for group-KB assignment +7. ✅ Update drive UI to show group access +8. ✅ Add tests for group-based access control + +## Security Considerations + +- All KB access checks must happen at the database level +- No client-side filtering of search results +- Group membership verified on each request +- Audit logging for KB access attempts + +## Testing + +- Unit tests for group access functions +- Integration tests for THINK KB with group filtering +- UI tests for admin group-KB management +- End-to-end tests with different user group scenarios \ No newline at end of file