@danhachuel/leadmaster 0.1.26 → 0.1.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/CHANGELOG.md +42 -19
  2. package/backend/__pycache__/main.cpython-311.pyc +0 -0
  3. package/backend/services/__pycache__/__init__.cpython-311.pyc +0 -0
  4. package/backend/services/__pycache__/agents.cpython-311.pyc +0 -0
  5. package/backend/services/__pycache__/crm_agent.cpython-311.pyc +0 -0
  6. package/backend/services/__pycache__/extractors.cpython-311.pyc +0 -0
  7. package/backend/services/__pycache__/flags.cpython-311.pyc +0 -0
  8. package/backend/services/__pycache__/llm.cpython-311.pyc +0 -0
  9. package/backend/services/__pycache__/storage.cpython-311.pyc +0 -0
  10. package/backend/tests/__pycache__/conftest.cpython-311-pytest-8.4.2.pyc +0 -0
  11. package/backend/tests/__pycache__/test_agents_routes.cpython-311-pytest-8.4.2.pyc +0 -0
  12. package/backend/tests/__pycache__/test_campaigns_routes.cpython-311-pytest-8.4.2.pyc +0 -0
  13. package/backend/tests/__pycache__/test_cnpj_extractor.cpython-311-pytest-8.4.2.pyc +0 -0
  14. package/backend/tests/__pycache__/test_config.cpython-311-pytest-8.4.2.pyc +0 -0
  15. package/backend/tests/__pycache__/test_crm_routes.cpython-311-pytest-8.4.2.pyc +0 -0
  16. package/backend/tests/__pycache__/test_extractors_real.cpython-311-pytest-8.4.2.pyc +0 -0
  17. package/backend/tests/__pycache__/test_funnel_routes.cpython-311-pytest-8.4.2.pyc +0 -0
  18. package/backend/tests/__pycache__/test_instagram_extractor.cpython-311-pytest-8.4.2.pyc +0 -0
  19. package/backend/tests/__pycache__/test_launcher_protections.cpython-311-pytest-8.4.2.pyc +0 -0
  20. package/backend/tests/__pycache__/test_lead_edit_routes.cpython-311-pytest-8.4.2.pyc +0 -0
  21. package/backend/tests/__pycache__/test_leads_export.cpython-311-pytest-8.4.2.pyc +0 -0
  22. package/backend/tests/__pycache__/test_maps_extractor.cpython-311-pytest-8.4.2.pyc +0 -0
  23. package/backend/tests/__pycache__/test_metrics.cpython-311-pytest-8.4.2.pyc +0 -0
  24. package/backend/tests/__pycache__/test_process_flags.cpython-311-pytest-8.4.2.pyc +0 -0
  25. package/backend/tests/__pycache__/test_webhook_routes.cpython-311-pytest-8.4.2.pyc +0 -0
  26. package/backend/tests/test_launcher_protections.py +12 -7
  27. package/frontend/dist/assets/{index-D5fh14uA.js → index-pSgnrc_D.js} +1 -1
  28. package/frontend/dist/index.html +1 -1
  29. package/package.json +1 -1
  30. package/scripts/cli.mjs +10 -20
package/CHANGELOG.md CHANGED
@@ -37,29 +37,52 @@ normaliza para a forma canônica (ex.: `0.1.0`).
37
37
  falha se `process.stdin.resume();` desaparecer, se `.unref()` aparecer, se o
38
38
  ciclo de vida vinculado for removido ou se os handlers globais sumirem.
39
39
 
40
+ ## [0.1.27] — 2026-11-09
41
+
42
+ ### Corrigido (crítico)
43
+
44
+ - **Launcher — Dummy Server (keep-alive infalível):** as versões anteriores
45
+ dependiam de `stdin`/timers; quando o MobaXterm fecha a entrada padrão em EOF
46
+ (perda de foco/variação de PTY), o event loop do Node esvaziava e o prompt
47
+ voltava logo após "LeadMaster no ar", derrubando o uvicorn junto. Agora o
48
+ `cmdStart` cria, **logo após o spawn do Python**, um servidor HTTP inativo
49
+ (`http.createServer` + `keepAliveServer.listen(0, '127.0.0.1')`) — o socket
50
+ aberto no sistema operacional segura o processo **independentemente do
51
+ terminal**; o fluxo principal termina em `await new Promise(() => {})`
52
+ (congelado para sempre) e `stdin.resume`/`setInterval`/`beforeExit` seguem
53
+ como camadas secundárias. **Testado ao vivo com stdin em EOF (`< NUL`):**
54
+ node vivo 1min46s+, health 200, exportação CSV 200 no ar, encerramento
55
+ limpo, zero órfãos.
56
+ - **AGENTS.md:** regra 1 do launcher passa a ser o Dummy Server (camada
57
+ principal); regra 2 rebaixa stdin/timers a camadas secundárias.
58
+ - **Gates de regressão reforçados** (`test_launcher_protections.py`): falham se
59
+ `http.createServer`, `keepAliveServer.listen(0, '127.0.0.1')` ou
60
+ `await new Promise(() => {})` desaparecerem do corpo do `cmdStart`.
61
+ Suíte completa: **83 passed**.
62
+
40
63
  ## [0.1.26] — 2026-11-09
41
64
 
42
65
  ### Adicionado
43
66
 
44
- - **Busca no CRM e exportação CSV:**
45
- - `GET /api/crm/leads/export`: gera CSV de todos os leads com **BOM UTF-8**
46
- (`\ufeff`, para o Excel em português não quebrar acentos), delimiter `;`,
47
- `Content-Type: text/csv; charset=utf-8` e
48
- `Content-Disposition: attachment; filename="leads_leadmaster.csv"`. Campos
49
- de `extras` são **achatados** como colunas `extras_<chave>` (união das
50
- chaves existentes) e listas (tags) viram células separadas por `; `.
51
- - Frontend: **barra de busca larga** no topo do CRM com filtragem local
52
- instantânea por **Nome, Telefone ou Email** + botão de limpar; **botão
53
- "Exportar CSV"** em estilo **neutro** (outline — não compete com o
54
- "Adicionar Lead" em gradiente, von Restorff) que baixa o arquivo via blob;
55
- busca sem resultados mostra aviso amigável *"Nenhum lead encontrado para
56
- '...'"* (Zeigarnik).
57
- - **Corrigido (pego pelos novos testes):** o modelo `LeadIn` descartava o campo
58
- `extras` — leads criados manualmente perdiam site/avaliação. Agora o POST de
59
- leads preserva `extras`.
60
- - **3 novos testes pytest** (`backend/tests/test_leads_export.py`): MIME +
61
- disposition + BOM + colunas, achatamento de `extras` e exportação de base
62
- vazia (só cabeçalho).
67
+ - **Busca no CRM e exportação CSV** (registro retro-ativo — publicada sem
68
+ entrada anterior):
69
+ - `GET /api/crm/leads/export`: CSV de todos os leads com **BOM UTF-8**
70
+ (`\ufeff`, Excel em português não quebra acentos), delimitador `;`,
71
+ `text/csv; charset=utf-8` e
72
+ `Content-Disposition: attachment; filename="leads_leadmaster.csv"`; campos
73
+ de `extras` **achatados** como colunas `extras_<chave>` e listas (tags) em
74
+ células separadas por `; `.
75
+ - Frontend: **barra de busca larga** no CRM com filtragem local instantânea
76
+ por **Nome, Telefone ou Email** + botão de limpar (Lei de Fitts); botão
77
+ **"Exportar CSV"** em estilo **neutro/outline** — não compete com o
78
+ "Adicionar Lead" em gradiente (von Restorff); busca sem resultados mostra
79
+ *"Nenhum lead encontrado para '...'"* (Zeigarnik).
80
+ - **Corrigido:** o modelo `LeadIn` descartava o campo `extras` — leads
81
+ criados manualmente perdiam site/avaliação. Agora o POST preserva
82
+ `extras` (`LeadIn.extras: dict`).
83
+ - **3 novos testes pytest** (`backend/tests/test_leads_export.py`): MIME +
84
+ disposition + BOM + colunas, achatamento de `extras` e exportação de base
85
+ vazia (só cabeçalho).
63
86
 
64
87
  ## [0.1.25] — 2026-11-09
65
88
 
@@ -54,13 +54,18 @@ class TestLauncherProtections:
54
54
  assert "já está rodando" not in c
55
55
 
56
56
  def test_keepalive_no_fluxo_do_start(self):
57
- # Gate de FLUXO (regressão da 0.1.21): as proteções precisam existir
58
- # dentro do cmdStart — no install, em função morta ou em bloco errado
59
- # não valem. A morte prematura do Node devolve o prompt do MobaXterm.
57
+ # Gate de FLUXO (regressões da 0.1.21/0.1.24): as proteções precisam
58
+ # existir dentro do cmdStart — no install, em função morta ou em bloco
59
+ # errado não valem. A morte prematura do Node devolve o prompt do
60
+ # MobaXterm. A camada PRINCIPAL é o Dummy Server (socket do SO);
61
+ # setInterval/stdin/beforeExit são secundárias.
60
62
  corpo = _corpo_cmd_start()
61
63
  assert "spawn(" in corpo # worker Python é spawnado no start
62
- assert "process.stdin.resume();" in corpo # terminal preso
63
- assert "setInterval(() => {}, 1000)" in corpo # keep-alive
64
+ assert "http.createServer" in corpo # Dummy Server: camada principal
65
+ assert "keepAliveServer.listen(0, '127.0.0.1')" in corpo
66
+ assert "await new Promise(() => {})" in corpo # fluxo principal congelado
67
+ assert "process.stdin.resume();" in corpo # camada secundária
68
+ assert "setInterval(() => {}, 1000)" in corpo # camada secundária
64
69
  assert "beforeExit" in corpo # rede de segurança do event loop
65
70
  assert "child.on('exit'" in corpo or 'child.on("exit"' in corpo
66
71
  assert "child.on('close'" in corpo or 'child.on("close"' in corpo
@@ -69,10 +74,10 @@ class TestLauncherProtections:
69
74
  def test_start_nunca_chama_exit_apos_terminal_preso(self):
70
75
  # Após o terminal ser preso, nenhum caminho de sucesso chama
71
76
  # process.exit — a saída só ocorre pelo ciclo de vida do filho
72
- # (child exit/close) ou por sinais de parada legítima.
77
+ # (child exit/close) ou por sinais de parada legítima (SIGTERM/SIGBREAK).
73
78
  corpo = _corpo_cmd_start()
74
79
  pos = corpo.index("process.stdin.resume();")
75
- fim_iife = corpo.index("})().catch(") # catch de erro fatal fica de fora
80
+ fim_iife = corpo.rindex("process.exit(1);") # catch de erro fatal, no fim
76
81
  assert "process.exit(" not in corpo[pos:fim_iife]
77
82
 
78
83
  def test_ciclo_de_vida_vinculado(self):
@@ -64,7 +64,7 @@ Error generating stack: `+i.message+`
64
64
  * LICENSE.md file in the root directory of this source tree.
65
65
  *
66
66
  * @license MIT
67
- */function zl(){return zl=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var s in n)({}).hasOwnProperty.call(n,s)&&(t[s]=n[s])}return t},zl.apply(null,arguments)}function yy(t,e){if(t==null)return{};var n={};for(var s in t)if({}.hasOwnProperty.call(t,s)){if(e.indexOf(s)!==-1)continue;n[s]=t[s]}return n}function YS(t){return!!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)}function XS(t,e){return t.button===0&&(!e||e==="_self")&&!YS(t)}const qS=["onClick","relative","reloadDocument","replace","state","target","to","preventScrollReset","viewTransition"],GS=["aria-current","caseSensitive","className","end","style","to","viewTransition","children"],JS="6";try{window.__reactRouterVersion=JS}catch{}const ZS=w.createContext({isTransitioning:!1}),e_="startTransition",Xp=ub[e_];function t_(t){let{basename:e,children:n,future:s,window:r}=t,i=w.useRef();i.current==null&&(i.current=oS({window:r,v5Compat:!0}));let o=i.current,[a,l]=w.useState({action:o.action,location:o.location}),{v7_startTransition:c}=s||{},d=w.useCallback(f=>{c&&Xp?Xp(()=>l(f)):l(f)},[l,c]);return w.useLayoutEffect(()=>o.listen(d),[o,d]),w.useEffect(()=>VS(s),[s]),w.createElement(QS,{basename:e,children:n,location:a.location,navigationType:a.action,navigator:o,future:s})}const n_=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",s_=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,r_=w.forwardRef(function(e,n){let{onClick:s,relative:r,reloadDocument:i,replace:o,state:a,target:l,to:c,preventScrollReset:d,viewTransition:f}=e,h=yy(e,qS),{basename:p}=w.useContext(Os),v,x=!1;if(typeof c=="string"&&s_.test(c)&&(v=c,n_))try{let m=new URL(window.location.href),_=c.startsWith("//")?new URL(m.protocol+c):new URL(c),b=mi(_.pathname,p);_.origin===m.origin&&b!=null?c=b+_.search+_.hash:x=!0}catch{}let S=PS(c,{relative:r}),y=o_(c,{replace:o,state:a,target:l,preventScrollReset:d,relative:r,viewTransition:f});function g(m){s&&s(m),m.defaultPrevented||y(m)}return w.createElement("a",zl({},h,{href:v||S,onClick:x||i?s:g,ref:n,target:l}))}),vy=w.forwardRef(function(e,n){let{"aria-current":s="page",caseSensitive:r=!1,className:i="",end:o=!1,style:a,to:l,viewTransition:c,children:d}=e,f=yy(e,GS),h=wc(l,{relative:f.relative}),p=hr(),v=w.useContext(fy),{navigator:x,basename:S}=w.useContext(Os),y=v!=null&&a_(h)&&c===!0,g=x.encodeLocation?x.encodeLocation(h).pathname:h.pathname,m=p.pathname,_=v&&v.navigation&&v.navigation.location?v.navigation.location.pathname:null;r||(m=m.toLowerCase(),_=_?_.toLowerCase():null,g=g.toLowerCase()),_&&S&&(_=mi(_,S)||_);const b=g!=="/"&&g.endsWith("/")?g.length-1:g.length;let k=m===g||!o&&m.startsWith(g)&&m.charAt(b)==="/",C=_!=null&&(_===g||!o&&_.startsWith(g)&&_.charAt(g.length)==="/"),N={isActive:k,isPending:C,isTransitioning:y},R=k?s:void 0,M;typeof i=="function"?M=i(N):M=[i,k?"active":null,C?"pending":null,y?"transitioning":null].filter(Boolean).join(" ");let E=typeof a=="function"?a(N):a;return w.createElement(r_,zl({},f,{"aria-current":R,className:M,ref:n,style:E,to:l,viewTransition:c}),typeof d=="function"?d(N):d)});var Ld;(function(t){t.UseScrollRestoration="useScrollRestoration",t.UseSubmit="useSubmit",t.UseSubmitFetcher="useSubmitFetcher",t.UseFetcher="useFetcher",t.useViewTransitionState="useViewTransitionState"})(Ld||(Ld={}));var qp;(function(t){t.UseFetcher="useFetcher",t.UseFetchers="useFetchers",t.UseScrollRestoration="useScrollRestoration"})(qp||(qp={}));function i_(t){let e=w.useContext(vc);return e||Ne(!1),e}function o_(t,e){let{target:n,replace:s,state:r,preventScrollReset:i,relative:o,viewTransition:a}=e===void 0?{}:e,l=oa(),c=hr(),d=wc(t,{relative:o});return w.useCallback(f=>{if(XS(f,n)){f.preventDefault();let h=s!==void 0?s:Fl(c)===Fl(d);l(t,{replace:h,state:r,preventScrollReset:i,relative:o,viewTransition:a})}},[c,l,d,s,r,n,t,i,o,a])}function a_(t,e){e===void 0&&(e={});let n=w.useContext(ZS);n==null&&Ne(!1);let{basename:s}=i_(Ld.useViewTransitionState),r=wc(t,{relative:e.relative});if(!n.isTransitioning)return!1;let i=mi(n.currentLocation.pathname,s)||n.currentLocation.pathname,o=mi(n.nextLocation.pathname,s)||n.nextLocation.pathname;return Td(r.pathname,o)!=null||Td(r.pathname,i)!=null}const l_="/api";class by extends Error{constructor(n,s){super(s);U(this,"status");this.status=n,this.name="ApiError"}}async function V(t,e={}){const n=await fetch(l_+t,{...e,headers:e.body?{"Content-Type":"application/json",...e.headers||{}}:e.headers});if(!n.ok){let s=`Erro HTTP ${n.status}`;try{const r=await n.json();s=String(r.detail||r.mensagem||s)}catch{}throw new by(n.status,s)}if(n.status!==204)return n.json()}const Ce=t=>({method:"POST",body:JSON.stringify(t)}),Q={health:()=>V("/health"),doctor:()=>V("/doctor"),shutdown:()=>V("/shutdown",Ce({})),metricas:()=>V("/metrics"),leads:{listar:()=>V("/leads"),criar:t=>V("/leads",Ce(t)),atualizar:(t,e)=>V(`/leads/${t}`,{method:"PATCH",body:JSON.stringify(e)}),remover:t=>V(`/leads/${t}`,{method:"DELETE"}),importar:t=>V("/leads/import",Ce({leads:t})),enriquecer:t=>V(`/leads/${t}/enrich`,Ce({}))},crm:{leadsListar:()=>V("/crm/leads"),criarLead:t=>V("/crm/leads",Ce(t)),editarLead:(t,e)=>V(`/crm/leads/${t}`,{method:"PUT",body:JSON.stringify(e)}),exportarCsvUrl:()=>"/api/crm/leads/export",moverLead:(t,e)=>V(`/crm/leads/${t}/etapa`,{method:"PUT",body:JSON.stringify({etapa:e})}),sugestoes:()=>V("/crm/sugestoes"),aplicar:(t,e)=>V("/crm/sugestoes/aplicar",Ce({lead_id:t,para:e}))},campanhas:{listar:()=>V("/campaigns"),criar:t=>V("/campaigns",Ce(t)),status:(t,e)=>V(`/campaigns/${t}/status`,{method:"PUT",body:JSON.stringify({status:e})}),iniciar:t=>V(`/campaigns/${t}/start`,Ce({})),pausar:t=>V(`/campaigns/${t}/pause`,Ce({})),parar:t=>V(`/campaigns/${t}/stop`,Ce({})),remover:t=>V(`/campaigns/${t}`,{method:"DELETE"})},extrair:{maps:t=>V("/extractors/maps",Ce(t)),instagram:(t,e)=>V("/extractors/instagram",Ce({profile_url:t,limit:e})),cnpj:t=>V("/extract/cnpj",Ce({cnpj:t})),cnpjBusca:t=>V("/extract/cnpj/search",Ce(t)),cnpjExtract:t=>V("/extractors/cnpj",Ce(t))},config:{obter:()=>V("/config"),salvar:t=>V("/config",{method:"PUT",body:JSON.stringify(t)}),testarLlm:()=>V("/config/test/llm",Ce({})),testarIntegracao:t=>V("/config/test/integracao",Ce({url:t}))},integracoes:()=>V("/integrations/status"),agentes:{listar:()=>V("/agents"),status:(t,e)=>V(`/agents/${t}/status`,Ce({status:e})),salvarPrompt:(t,e)=>V(`/agents/${t}/prompt`,Ce({prompt:e})),templates:()=>V("/agents/templates"),salvarTemplates:t=>V("/agents/templates",{method:"PUT",body:JSON.stringify(t)}),testarTemplate:(t,e)=>V("/agents/templates/test",Ce({template:t,lead_id:e})),chat:t=>V("/agents/attendance/chat",Ce({mensagens:t}))},funil:()=>V("/funnel"),webhook:{obter:()=>V("/webhook"),salvar:t=>V("/webhook",{method:"PUT",body:JSON.stringify(t)}),testar:t=>V("/webhook/test",Ce({url:t}))}};function H(...t){return t.filter(Boolean).join(" ")}function c_(t){return(t||"?").trim().charAt(0).toUpperCase()||"?"}function $o(t){if(!t)return"—";try{return new Date(t).toLocaleString("pt-BR",{day:"2-digit",month:"2-digit",year:"2-digit",hour:"2-digit",minute:"2-digit"})}catch{return t}}function Sc(t){const e=(t||"").replace(/\D/g,"");return e?e.length===11?`(${e.slice(0,2)}) ${e.slice(2,7)}-${e.slice(7)}`:e.length===10?`(${e.slice(0,2)}) ${e.slice(2,6)}-${e.slice(6)}`:t||"—":"—"}function u_(t){const e=["nome","telefone","email","empresa","cidade","observacoes"],n=e.filter(s=>String(t[s]||"").trim().length>0);return Math.round(n.length/e.length*100)}function d_(t){const e={nome:"nome",telefone:"telefone",email:"e-mail",empresa:"empresa",cidade:"cidade",observacoes:"observações"};return["nome","telefone","email","empresa","cidade","observacoes"].filter(s=>String(t[s]||"").trim().length===0).map(s=>e[s])}function f_(t,e){if(t>=100)return"Lead 100% completo — excelente! 🎉";const n=e.slice(0,2).join(" e ");return`Faltam ${100-t}% (${n}${e.length>2?"...":""}) para o lead ficar completo`}function h_(t,e){const n=new Blob([JSON.stringify(t,null,2)],{type:"application/json"}),s=URL.createObjectURL(n),r=document.createElement("a");r.href=s,r.download=e,r.click(),URL.revokeObjectURL(s)}const p_={md:"max-w-lg",lg:"max-w-2xl",xl:"max-w-4xl"};function qt({aberto:t,onFechar:e,titulo:n,children:s,icone:r="fas fa-circle-info",largura:i="md"}){return w.useEffect(()=>{const o=a=>{a.key==="Escape"&&e()};return t&&window.addEventListener("keydown",o),()=>window.removeEventListener("keydown",o)},[t,e]),t?u.jsx("div",{className:"fixed inset-0 z-50 bg-black/40 flex items-center justify-center p-4",onMouseDown:o=>{o.target===o.currentTarget&&e()},children:u.jsxs("div",{className:H("bg-white rounded-2xl shadow-xl w-full",p_[i]),children:[u.jsxs("div",{className:"flex items-center justify-between px-6 py-4 border-b border-gray-100",children:[u.jsxs("h3",{className:"text-base font-semibold text-gray-800 flex items-center gap-2",children:[u.jsx("i",{className:H(r,"text-indigo-500")})," ",n]}),u.jsx("button",{onClick:e,className:"text-gray-400 hover:text-gray-600","aria-label":"Fechar",children:u.jsx("i",{className:"fas fa-times"})})]}),u.jsx("div",{className:"px-6 py-5 max-h-[70vh] overflow-y-auto",children:s})]})}):null}const m_=[{icone:"fas fa-users",texto:"Cadastre leads manualmente ou importe pelos Extratores (Maps, Instagram, CNPJ)."},{icone:"fas fa-columns",texto:"Arraste os cards no CRM conforme avança no funil (Leads → Em contato → Proposta → Fechado)."},{icone:"fas fa-bullhorn",texto:"Monte campanhas de disparo com seus templates e acompanhe as taxas no Painel."},{icone:"fas fa-robot",texto:"Use os Agentes (disparo e atendimento) e configure o LLM em Ferramentas para turbiná-los."}];function g_({titulo:t,nomeUsuario:e}){const[n,s]=w.useState(!1);return u.jsxs("header",{className:"bg-white shadow-sm px-8 py-4 flex items-center justify-between shrink-0",children:[u.jsx("div",{children:u.jsx("h2",{className:"text-lg font-semibold text-gray-800",children:t})}),u.jsxs("div",{className:"flex items-center space-x-4",children:[u.jsxs("button",{onClick:()=>s(!0),className:"text-sm text-indigo-600 hover:text-indigo-800 font-medium",children:[u.jsx("i",{className:"fas fa-play-circle mr-1"})," Ver tutorial"]}),u.jsx("div",{className:"w-8 h-8 rounded-full bg-indigo-100 flex items-center justify-center text-indigo-600 font-bold text-sm",children:c_(e)}),u.jsx("span",{className:"text-sm font-medium text-gray-700",children:e})]}),u.jsxs(qt,{aberto:n,onFechar:()=>s(!1),titulo:"Tutorial rápido",icone:"fas fa-graduation-cap",children:[u.jsx("ol",{className:"space-y-4",children:m_.map((r,i)=>u.jsxs("li",{className:"flex gap-3 items-start",children:[u.jsx("span",{className:"w-8 h-8 rounded-full bg-indigo-100 text-indigo-600 flex items-center justify-center shrink-0",children:u.jsx("i",{className:r.icone})}),u.jsx("p",{className:"text-sm text-gray-600 pt-1.5",children:r.texto})]},i))}),u.jsx("p",{className:"text-xs text-gray-400 mt-5",children:"Dica: o LLM (para enriquecimento e agentes) é configurado em Ferramentas."})]})]})}const x_=[{itens:[{para:"/",icone:"fas fa-chart-pie text-indigo-300",rotulo:"Painel",fim:!0}]},{secao:"Conexões",itens:[{para:"/leads",icone:"fas fa-users",rotulo:"Leads"},{para:"/agentes",icone:"fas fa-robot",rotulo:"Agentes"},{para:"/ferramentas",icone:"fas fa-tools",rotulo:"Ferramentas"}]},{itens:[{para:"/extrator-maps",icone:"fas fa-map-marked-alt",rotulo:"Extrator Maps"},{para:"/extrator-instagram",icone:"fab fa-instagram",rotulo:"Extrator Instagram"},{para:"/extrator-cnpj",icone:"fas fa-building",rotulo:"Extrator CNPJ"}]},{secao:"Gestão",itens:[{para:"/disparos",icone:"fas fa-bullhorn",rotulo:"Disparos"},{para:"/crm",icone:"fas fa-columns",rotulo:"CRM"},{para:"/funil",icone:"fas fa-filter",rotulo:"Funil de Prospecção"}]},{secao:"Integrações",itens:[{para:"/webhook",icone:"fas fa-link",rotulo:"Webhook"},{para:"/suporte",icone:"fas fa-headset",rotulo:"Suporte"}]}];function y_({onSair:t}){const e=hr(),[n,s]=w.useState(!1);w.useEffect(()=>{e.pathname==="/agentes"&&s(!0)},[e.pathname]);const r=i=>{if(i.rotulo==="Agentes")return e.pathname==="/agentes";const[o,a]=i.para.split("?");return a?e.pathname===o&&e.search.includes(a):i.fim?e.pathname===o:e.pathname.startsWith(o)};return u.jsxs("aside",{className:"w-64 bg-[#1e1b4b] text-white flex flex-col shrink-0 h-screen sticky top-0",children:[u.jsx("div",{className:"p-6 border-b border-gray-700",children:u.jsxs("h1",{className:"text-2xl font-bold tracking-tight flex items-center gap-2",children:[u.jsx("span",{className:"text-2xl",children:"⚡"}),"LeadMaster",u.jsxs("span",{className:"text-[10px] font-medium text-indigo-300 border border-indigo-400/40 rounded-md px-1.5 py-0.5",children:["v","0.1.26"]})]})}),u.jsx("nav",{className:"flex-1 overflow-y-auto py-4",children:u.jsx("ul",{className:"space-y-1",children:x_.map((i,o)=>u.jsx("li",{children:u.jsxs("ul",{children:[i.secao&&u.jsx("li",{className:"px-6 py-2 text-xs font-semibold text-gray-400 uppercase tracking-wider list-none",children:i.secao}),i.itens.map(a=>a.rotulo==="Agentes"?u.jsxs("li",{children:[u.jsxs("button",{type:"button",onClick:()=>s(l=>!l),className:H("sidebar-link w-full flex items-center px-6 py-2.5 text-sm",r(a)&&"active"),children:[u.jsx("i",{className:H(a.icone,"w-5 text-gray-400")}),u.jsx("span",{className:"ml-3",children:a.rotulo}),u.jsx("i",{className:H("fas fa-chevron-down ml-auto text-xs text-gray-400 transition-transform",n&&"rotate-180")})]}),n&&u.jsxs("ul",{className:"pl-12 space-y-1 mt-1",children:[u.jsx("li",{children:u.jsx(Gp,{para:"/agentes?tab=disparo",rotulo:"Agente de Disparo",ativo:e.pathname==="/agentes"&&e.search.includes("disparo")})}),u.jsx("li",{children:u.jsx(Gp,{para:"/agentes?tab=atendimento",rotulo:"Agente de Atendimento",ativo:e.pathname==="/agentes"&&e.search.includes("atendimento")})})]})]},"agentes"):u.jsx("li",{children:u.jsxs(vy,{to:a.para,className:H("sidebar-link flex items-center px-6 py-2.5 text-sm",r(a)&&"active"),children:[u.jsx("i",{className:H(a.icone,"w-5 text-gray-400")}),u.jsx("span",{className:"ml-3",children:a.rotulo})]})},a.para))]})},o))})}),u.jsx("div",{className:"p-4 border-t border-gray-700",children:u.jsxs("button",{onClick:t,className:"w-full flex items-center justify-center px-4 py-2.5 bg-gradient-to-r from-indigo-500 to-purple-500 rounded-lg text-sm font-medium hover:opacity-90 transition",children:[u.jsx("i",{className:"fas fa-sign-out-alt mr-2"})," Sair"]})})]})}function Gp({para:t,rotulo:e,ativo:n}){return u.jsx(vy,{to:t,className:H("sidebar-link block px-4 py-2 text-xs text-gray-300 rounded",n&&"active"),children:e})}const wy=w.createContext({toast:()=>{}});function tn(){return w.useContext(wy)}const v_={sucesso:"bg-green-600",erro:"bg-red-600",info:"bg-indigo-600"},b_={sucesso:"fas fa-check-circle",erro:"fas fa-exclamation-circle",info:"fas fa-info-circle"};function w_({children:t}){const[e,n]=w.useState([]),s=w.useRef(0),r=w.useCallback((i,o)=>{const a=++s.current;n(l=>[...l,{id:a,tipo:i,mensagem:o}]),setTimeout(()=>n(l=>l.filter(c=>c.id!==a)),4500)},[]);return u.jsxs(wy.Provider,{value:{toast:r},children:[t,u.jsx("div",{className:"fixed top-4 right-4 z-[100] space-y-2 w-80",children:e.map(i=>u.jsxs("div",{className:H(v_[i.tipo],"text-white text-sm rounded-lg shadow-lg px-4 py-3 flex items-start gap-2 animate-pulse-once"),role:"status",children:[u.jsx("i",{className:H(b_[i.tipo],"mt-0.5")}),u.jsx("span",{className:"flex-1",children:i.mensagem}),u.jsx("button",{className:"opacity-70 hover:opacity-100",onClick:()=>n(o=>o.filter(a=>a.id!==i.id)),"aria-label":"Fechar",children:u.jsx("i",{className:"fas fa-times"})})]},i.id))})]})}function Ta({rotulo:t,valor:e,subtitulo:n,children:s,icone:r}){return u.jsxs("div",{className:"bg-white rounded-xl shadow-sm p-5 border border-gray-100",children:[u.jsxs("div",{className:"flex items-center justify-between",children:[u.jsx("p",{className:"text-sm text-gray-500 font-medium",children:t}),r&&u.jsx("i",{className:`${r} text-indigo-300`})]}),s??u.jsxs(u.Fragment,{children:[u.jsx("p",{className:"text-2xl font-bold text-gray-800 mt-1",children:e}),n&&u.jsx("p",{className:"text-xs text-gray-400 mt-0.5",children:n})]})]})}/*!
67
+ */function zl(){return zl=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var s in n)({}).hasOwnProperty.call(n,s)&&(t[s]=n[s])}return t},zl.apply(null,arguments)}function yy(t,e){if(t==null)return{};var n={};for(var s in t)if({}.hasOwnProperty.call(t,s)){if(e.indexOf(s)!==-1)continue;n[s]=t[s]}return n}function YS(t){return!!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)}function XS(t,e){return t.button===0&&(!e||e==="_self")&&!YS(t)}const qS=["onClick","relative","reloadDocument","replace","state","target","to","preventScrollReset","viewTransition"],GS=["aria-current","caseSensitive","className","end","style","to","viewTransition","children"],JS="6";try{window.__reactRouterVersion=JS}catch{}const ZS=w.createContext({isTransitioning:!1}),e_="startTransition",Xp=ub[e_];function t_(t){let{basename:e,children:n,future:s,window:r}=t,i=w.useRef();i.current==null&&(i.current=oS({window:r,v5Compat:!0}));let o=i.current,[a,l]=w.useState({action:o.action,location:o.location}),{v7_startTransition:c}=s||{},d=w.useCallback(f=>{c&&Xp?Xp(()=>l(f)):l(f)},[l,c]);return w.useLayoutEffect(()=>o.listen(d),[o,d]),w.useEffect(()=>VS(s),[s]),w.createElement(QS,{basename:e,children:n,location:a.location,navigationType:a.action,navigator:o,future:s})}const n_=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",s_=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,r_=w.forwardRef(function(e,n){let{onClick:s,relative:r,reloadDocument:i,replace:o,state:a,target:l,to:c,preventScrollReset:d,viewTransition:f}=e,h=yy(e,qS),{basename:p}=w.useContext(Os),v,x=!1;if(typeof c=="string"&&s_.test(c)&&(v=c,n_))try{let m=new URL(window.location.href),_=c.startsWith("//")?new URL(m.protocol+c):new URL(c),b=mi(_.pathname,p);_.origin===m.origin&&b!=null?c=b+_.search+_.hash:x=!0}catch{}let S=PS(c,{relative:r}),y=o_(c,{replace:o,state:a,target:l,preventScrollReset:d,relative:r,viewTransition:f});function g(m){s&&s(m),m.defaultPrevented||y(m)}return w.createElement("a",zl({},h,{href:v||S,onClick:x||i?s:g,ref:n,target:l}))}),vy=w.forwardRef(function(e,n){let{"aria-current":s="page",caseSensitive:r=!1,className:i="",end:o=!1,style:a,to:l,viewTransition:c,children:d}=e,f=yy(e,GS),h=wc(l,{relative:f.relative}),p=hr(),v=w.useContext(fy),{navigator:x,basename:S}=w.useContext(Os),y=v!=null&&a_(h)&&c===!0,g=x.encodeLocation?x.encodeLocation(h).pathname:h.pathname,m=p.pathname,_=v&&v.navigation&&v.navigation.location?v.navigation.location.pathname:null;r||(m=m.toLowerCase(),_=_?_.toLowerCase():null,g=g.toLowerCase()),_&&S&&(_=mi(_,S)||_);const b=g!=="/"&&g.endsWith("/")?g.length-1:g.length;let k=m===g||!o&&m.startsWith(g)&&m.charAt(b)==="/",C=_!=null&&(_===g||!o&&_.startsWith(g)&&_.charAt(g.length)==="/"),N={isActive:k,isPending:C,isTransitioning:y},R=k?s:void 0,M;typeof i=="function"?M=i(N):M=[i,k?"active":null,C?"pending":null,y?"transitioning":null].filter(Boolean).join(" ");let E=typeof a=="function"?a(N):a;return w.createElement(r_,zl({},f,{"aria-current":R,className:M,ref:n,style:E,to:l,viewTransition:c}),typeof d=="function"?d(N):d)});var Ld;(function(t){t.UseScrollRestoration="useScrollRestoration",t.UseSubmit="useSubmit",t.UseSubmitFetcher="useSubmitFetcher",t.UseFetcher="useFetcher",t.useViewTransitionState="useViewTransitionState"})(Ld||(Ld={}));var qp;(function(t){t.UseFetcher="useFetcher",t.UseFetchers="useFetchers",t.UseScrollRestoration="useScrollRestoration"})(qp||(qp={}));function i_(t){let e=w.useContext(vc);return e||Ne(!1),e}function o_(t,e){let{target:n,replace:s,state:r,preventScrollReset:i,relative:o,viewTransition:a}=e===void 0?{}:e,l=oa(),c=hr(),d=wc(t,{relative:o});return w.useCallback(f=>{if(XS(f,n)){f.preventDefault();let h=s!==void 0?s:Fl(c)===Fl(d);l(t,{replace:h,state:r,preventScrollReset:i,relative:o,viewTransition:a})}},[c,l,d,s,r,n,t,i,o,a])}function a_(t,e){e===void 0&&(e={});let n=w.useContext(ZS);n==null&&Ne(!1);let{basename:s}=i_(Ld.useViewTransitionState),r=wc(t,{relative:e.relative});if(!n.isTransitioning)return!1;let i=mi(n.currentLocation.pathname,s)||n.currentLocation.pathname,o=mi(n.nextLocation.pathname,s)||n.nextLocation.pathname;return Td(r.pathname,o)!=null||Td(r.pathname,i)!=null}const l_="/api";class by extends Error{constructor(n,s){super(s);U(this,"status");this.status=n,this.name="ApiError"}}async function V(t,e={}){const n=await fetch(l_+t,{...e,headers:e.body?{"Content-Type":"application/json",...e.headers||{}}:e.headers});if(!n.ok){let s=`Erro HTTP ${n.status}`;try{const r=await n.json();s=String(r.detail||r.mensagem||s)}catch{}throw new by(n.status,s)}if(n.status!==204)return n.json()}const Ce=t=>({method:"POST",body:JSON.stringify(t)}),Q={health:()=>V("/health"),doctor:()=>V("/doctor"),shutdown:()=>V("/shutdown",Ce({})),metricas:()=>V("/metrics"),leads:{listar:()=>V("/leads"),criar:t=>V("/leads",Ce(t)),atualizar:(t,e)=>V(`/leads/${t}`,{method:"PATCH",body:JSON.stringify(e)}),remover:t=>V(`/leads/${t}`,{method:"DELETE"}),importar:t=>V("/leads/import",Ce({leads:t})),enriquecer:t=>V(`/leads/${t}/enrich`,Ce({}))},crm:{leadsListar:()=>V("/crm/leads"),criarLead:t=>V("/crm/leads",Ce(t)),editarLead:(t,e)=>V(`/crm/leads/${t}`,{method:"PUT",body:JSON.stringify(e)}),exportarCsvUrl:()=>"/api/crm/leads/export",moverLead:(t,e)=>V(`/crm/leads/${t}/etapa`,{method:"PUT",body:JSON.stringify({etapa:e})}),sugestoes:()=>V("/crm/sugestoes"),aplicar:(t,e)=>V("/crm/sugestoes/aplicar",Ce({lead_id:t,para:e}))},campanhas:{listar:()=>V("/campaigns"),criar:t=>V("/campaigns",Ce(t)),status:(t,e)=>V(`/campaigns/${t}/status`,{method:"PUT",body:JSON.stringify({status:e})}),iniciar:t=>V(`/campaigns/${t}/start`,Ce({})),pausar:t=>V(`/campaigns/${t}/pause`,Ce({})),parar:t=>V(`/campaigns/${t}/stop`,Ce({})),remover:t=>V(`/campaigns/${t}`,{method:"DELETE"})},extrair:{maps:t=>V("/extractors/maps",Ce(t)),instagram:(t,e)=>V("/extractors/instagram",Ce({profile_url:t,limit:e})),cnpj:t=>V("/extract/cnpj",Ce({cnpj:t})),cnpjBusca:t=>V("/extract/cnpj/search",Ce(t)),cnpjExtract:t=>V("/extractors/cnpj",Ce(t))},config:{obter:()=>V("/config"),salvar:t=>V("/config",{method:"PUT",body:JSON.stringify(t)}),testarLlm:()=>V("/config/test/llm",Ce({})),testarIntegracao:t=>V("/config/test/integracao",Ce({url:t}))},integracoes:()=>V("/integrations/status"),agentes:{listar:()=>V("/agents"),status:(t,e)=>V(`/agents/${t}/status`,Ce({status:e})),salvarPrompt:(t,e)=>V(`/agents/${t}/prompt`,Ce({prompt:e})),templates:()=>V("/agents/templates"),salvarTemplates:t=>V("/agents/templates",{method:"PUT",body:JSON.stringify(t)}),testarTemplate:(t,e)=>V("/agents/templates/test",Ce({template:t,lead_id:e})),chat:t=>V("/agents/attendance/chat",Ce({mensagens:t}))},funil:()=>V("/funnel"),webhook:{obter:()=>V("/webhook"),salvar:t=>V("/webhook",{method:"PUT",body:JSON.stringify(t)}),testar:t=>V("/webhook/test",Ce({url:t}))}};function H(...t){return t.filter(Boolean).join(" ")}function c_(t){return(t||"?").trim().charAt(0).toUpperCase()||"?"}function $o(t){if(!t)return"—";try{return new Date(t).toLocaleString("pt-BR",{day:"2-digit",month:"2-digit",year:"2-digit",hour:"2-digit",minute:"2-digit"})}catch{return t}}function Sc(t){const e=(t||"").replace(/\D/g,"");return e?e.length===11?`(${e.slice(0,2)}) ${e.slice(2,7)}-${e.slice(7)}`:e.length===10?`(${e.slice(0,2)}) ${e.slice(2,6)}-${e.slice(6)}`:t||"—":"—"}function u_(t){const e=["nome","telefone","email","empresa","cidade","observacoes"],n=e.filter(s=>String(t[s]||"").trim().length>0);return Math.round(n.length/e.length*100)}function d_(t){const e={nome:"nome",telefone:"telefone",email:"e-mail",empresa:"empresa",cidade:"cidade",observacoes:"observações"};return["nome","telefone","email","empresa","cidade","observacoes"].filter(s=>String(t[s]||"").trim().length===0).map(s=>e[s])}function f_(t,e){if(t>=100)return"Lead 100% completo — excelente! 🎉";const n=e.slice(0,2).join(" e ");return`Faltam ${100-t}% (${n}${e.length>2?"...":""}) para o lead ficar completo`}function h_(t,e){const n=new Blob([JSON.stringify(t,null,2)],{type:"application/json"}),s=URL.createObjectURL(n),r=document.createElement("a");r.href=s,r.download=e,r.click(),URL.revokeObjectURL(s)}const p_={md:"max-w-lg",lg:"max-w-2xl",xl:"max-w-4xl"};function qt({aberto:t,onFechar:e,titulo:n,children:s,icone:r="fas fa-circle-info",largura:i="md"}){return w.useEffect(()=>{const o=a=>{a.key==="Escape"&&e()};return t&&window.addEventListener("keydown",o),()=>window.removeEventListener("keydown",o)},[t,e]),t?u.jsx("div",{className:"fixed inset-0 z-50 bg-black/40 flex items-center justify-center p-4",onMouseDown:o=>{o.target===o.currentTarget&&e()},children:u.jsxs("div",{className:H("bg-white rounded-2xl shadow-xl w-full",p_[i]),children:[u.jsxs("div",{className:"flex items-center justify-between px-6 py-4 border-b border-gray-100",children:[u.jsxs("h3",{className:"text-base font-semibold text-gray-800 flex items-center gap-2",children:[u.jsx("i",{className:H(r,"text-indigo-500")})," ",n]}),u.jsx("button",{onClick:e,className:"text-gray-400 hover:text-gray-600","aria-label":"Fechar",children:u.jsx("i",{className:"fas fa-times"})})]}),u.jsx("div",{className:"px-6 py-5 max-h-[70vh] overflow-y-auto",children:s})]})}):null}const m_=[{icone:"fas fa-users",texto:"Cadastre leads manualmente ou importe pelos Extratores (Maps, Instagram, CNPJ)."},{icone:"fas fa-columns",texto:"Arraste os cards no CRM conforme avança no funil (Leads → Em contato → Proposta → Fechado)."},{icone:"fas fa-bullhorn",texto:"Monte campanhas de disparo com seus templates e acompanhe as taxas no Painel."},{icone:"fas fa-robot",texto:"Use os Agentes (disparo e atendimento) e configure o LLM em Ferramentas para turbiná-los."}];function g_({titulo:t,nomeUsuario:e}){const[n,s]=w.useState(!1);return u.jsxs("header",{className:"bg-white shadow-sm px-8 py-4 flex items-center justify-between shrink-0",children:[u.jsx("div",{children:u.jsx("h2",{className:"text-lg font-semibold text-gray-800",children:t})}),u.jsxs("div",{className:"flex items-center space-x-4",children:[u.jsxs("button",{onClick:()=>s(!0),className:"text-sm text-indigo-600 hover:text-indigo-800 font-medium",children:[u.jsx("i",{className:"fas fa-play-circle mr-1"})," Ver tutorial"]}),u.jsx("div",{className:"w-8 h-8 rounded-full bg-indigo-100 flex items-center justify-center text-indigo-600 font-bold text-sm",children:c_(e)}),u.jsx("span",{className:"text-sm font-medium text-gray-700",children:e})]}),u.jsxs(qt,{aberto:n,onFechar:()=>s(!1),titulo:"Tutorial rápido",icone:"fas fa-graduation-cap",children:[u.jsx("ol",{className:"space-y-4",children:m_.map((r,i)=>u.jsxs("li",{className:"flex gap-3 items-start",children:[u.jsx("span",{className:"w-8 h-8 rounded-full bg-indigo-100 text-indigo-600 flex items-center justify-center shrink-0",children:u.jsx("i",{className:r.icone})}),u.jsx("p",{className:"text-sm text-gray-600 pt-1.5",children:r.texto})]},i))}),u.jsx("p",{className:"text-xs text-gray-400 mt-5",children:"Dica: o LLM (para enriquecimento e agentes) é configurado em Ferramentas."})]})]})}const x_=[{itens:[{para:"/",icone:"fas fa-chart-pie text-indigo-300",rotulo:"Painel",fim:!0}]},{secao:"Conexões",itens:[{para:"/leads",icone:"fas fa-users",rotulo:"Leads"},{para:"/agentes",icone:"fas fa-robot",rotulo:"Agentes"},{para:"/ferramentas",icone:"fas fa-tools",rotulo:"Ferramentas"}]},{itens:[{para:"/extrator-maps",icone:"fas fa-map-marked-alt",rotulo:"Extrator Maps"},{para:"/extrator-instagram",icone:"fab fa-instagram",rotulo:"Extrator Instagram"},{para:"/extrator-cnpj",icone:"fas fa-building",rotulo:"Extrator CNPJ"}]},{secao:"Gestão",itens:[{para:"/disparos",icone:"fas fa-bullhorn",rotulo:"Disparos"},{para:"/crm",icone:"fas fa-columns",rotulo:"CRM"},{para:"/funil",icone:"fas fa-filter",rotulo:"Funil de Prospecção"}]},{secao:"Integrações",itens:[{para:"/webhook",icone:"fas fa-link",rotulo:"Webhook"},{para:"/suporte",icone:"fas fa-headset",rotulo:"Suporte"}]}];function y_({onSair:t}){const e=hr(),[n,s]=w.useState(!1);w.useEffect(()=>{e.pathname==="/agentes"&&s(!0)},[e.pathname]);const r=i=>{if(i.rotulo==="Agentes")return e.pathname==="/agentes";const[o,a]=i.para.split("?");return a?e.pathname===o&&e.search.includes(a):i.fim?e.pathname===o:e.pathname.startsWith(o)};return u.jsxs("aside",{className:"w-64 bg-[#1e1b4b] text-white flex flex-col shrink-0 h-screen sticky top-0",children:[u.jsx("div",{className:"p-6 border-b border-gray-700",children:u.jsxs("h1",{className:"text-2xl font-bold tracking-tight flex items-center gap-2",children:[u.jsx("span",{className:"text-2xl",children:"⚡"}),"LeadMaster",u.jsxs("span",{className:"text-[10px] font-medium text-indigo-300 border border-indigo-400/40 rounded-md px-1.5 py-0.5",children:["v","0.1.27"]})]})}),u.jsx("nav",{className:"flex-1 overflow-y-auto py-4",children:u.jsx("ul",{className:"space-y-1",children:x_.map((i,o)=>u.jsx("li",{children:u.jsxs("ul",{children:[i.secao&&u.jsx("li",{className:"px-6 py-2 text-xs font-semibold text-gray-400 uppercase tracking-wider list-none",children:i.secao}),i.itens.map(a=>a.rotulo==="Agentes"?u.jsxs("li",{children:[u.jsxs("button",{type:"button",onClick:()=>s(l=>!l),className:H("sidebar-link w-full flex items-center px-6 py-2.5 text-sm",r(a)&&"active"),children:[u.jsx("i",{className:H(a.icone,"w-5 text-gray-400")}),u.jsx("span",{className:"ml-3",children:a.rotulo}),u.jsx("i",{className:H("fas fa-chevron-down ml-auto text-xs text-gray-400 transition-transform",n&&"rotate-180")})]}),n&&u.jsxs("ul",{className:"pl-12 space-y-1 mt-1",children:[u.jsx("li",{children:u.jsx(Gp,{para:"/agentes?tab=disparo",rotulo:"Agente de Disparo",ativo:e.pathname==="/agentes"&&e.search.includes("disparo")})}),u.jsx("li",{children:u.jsx(Gp,{para:"/agentes?tab=atendimento",rotulo:"Agente de Atendimento",ativo:e.pathname==="/agentes"&&e.search.includes("atendimento")})})]})]},"agentes"):u.jsx("li",{children:u.jsxs(vy,{to:a.para,className:H("sidebar-link flex items-center px-6 py-2.5 text-sm",r(a)&&"active"),children:[u.jsx("i",{className:H(a.icone,"w-5 text-gray-400")}),u.jsx("span",{className:"ml-3",children:a.rotulo})]})},a.para))]})},o))})}),u.jsx("div",{className:"p-4 border-t border-gray-700",children:u.jsxs("button",{onClick:t,className:"w-full flex items-center justify-center px-4 py-2.5 bg-gradient-to-r from-indigo-500 to-purple-500 rounded-lg text-sm font-medium hover:opacity-90 transition",children:[u.jsx("i",{className:"fas fa-sign-out-alt mr-2"})," Sair"]})})]})}function Gp({para:t,rotulo:e,ativo:n}){return u.jsx(vy,{to:t,className:H("sidebar-link block px-4 py-2 text-xs text-gray-300 rounded",n&&"active"),children:e})}const wy=w.createContext({toast:()=>{}});function tn(){return w.useContext(wy)}const v_={sucesso:"bg-green-600",erro:"bg-red-600",info:"bg-indigo-600"},b_={sucesso:"fas fa-check-circle",erro:"fas fa-exclamation-circle",info:"fas fa-info-circle"};function w_({children:t}){const[e,n]=w.useState([]),s=w.useRef(0),r=w.useCallback((i,o)=>{const a=++s.current;n(l=>[...l,{id:a,tipo:i,mensagem:o}]),setTimeout(()=>n(l=>l.filter(c=>c.id!==a)),4500)},[]);return u.jsxs(wy.Provider,{value:{toast:r},children:[t,u.jsx("div",{className:"fixed top-4 right-4 z-[100] space-y-2 w-80",children:e.map(i=>u.jsxs("div",{className:H(v_[i.tipo],"text-white text-sm rounded-lg shadow-lg px-4 py-3 flex items-start gap-2 animate-pulse-once"),role:"status",children:[u.jsx("i",{className:H(b_[i.tipo],"mt-0.5")}),u.jsx("span",{className:"flex-1",children:i.mensagem}),u.jsx("button",{className:"opacity-70 hover:opacity-100",onClick:()=>n(o=>o.filter(a=>a.id!==i.id)),"aria-label":"Fechar",children:u.jsx("i",{className:"fas fa-times"})})]},i.id))})]})}function Ta({rotulo:t,valor:e,subtitulo:n,children:s,icone:r}){return u.jsxs("div",{className:"bg-white rounded-xl shadow-sm p-5 border border-gray-100",children:[u.jsxs("div",{className:"flex items-center justify-between",children:[u.jsx("p",{className:"text-sm text-gray-500 font-medium",children:t}),r&&u.jsx("i",{className:`${r} text-indigo-300`})]}),s??u.jsxs(u.Fragment,{children:[u.jsx("p",{className:"text-2xl font-bold text-gray-800 mt-1",children:e}),n&&u.jsx("p",{className:"text-xs text-gray-400 mt-0.5",children:n})]})]})}/*!
68
68
  * @kurkle/color v0.3.4
69
69
  * https://github.com/kurkle/color#readme
70
70
  * (c) 2024 Jukka Kurkela
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
7
7
  <title>LeadMaster — Painel</title>
8
- <script type="module" crossorigin src="/assets/index-D5fh14uA.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-pSgnrc_D.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/assets/index-NeGNWLr8.css">
10
10
  </head>
11
11
  <body class="bg-gray-100 min-h-screen">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/leadmaster",
3
- "version": "0.1.26",
3
+ "version": "0.1.27",
4
4
  "description": "SaaS local de prospecção e automação: CRM, extratores de leads e agentes de IA — 100% local.",
5
5
  "license": "MIT",
6
6
  "author": "Dan Hachuel",
package/scripts/cli.mjs CHANGED
@@ -672,11 +672,10 @@ function cmdStart(args) {
672
672
  }
673
673
 
674
674
  // -------------------------------------------------------------------------
675
- // Sinais (regra inviolável do AGENTS.md)
676
- // MobaXterm envia SIGINT/SIGHUP espúrios (perda de foco, minimização): no
677
- // Windows esses sinais são APENAS ABSORVIDOS (log), sem encerrar o launcher,
678
- // para o app não morrer sozinho. SIGTERM e SIGBREAK são paradas legítimas e
679
- // sempre encerram. No Linux/macOS, SIGINT/SIGHUP mantêm o stop gracioso.
675
+ // Camadas SECUNDÁRIAS de keep-alive (o Dummy Server acima é a principal):
676
+ // setInterval mantém o loop ativo, stdin.resume ouve digitação, e o
677
+ // beforeExit re-arma o timer se o loop esvaziar. Erros de stdin são
678
+ // ignorados — nenhum deles pode derrubar o launcher.
680
679
  // -------------------------------------------------------------------------
681
680
  const ehWindows = platform() === 'win32';
682
681
  const naoAbsorver = () => { stop(); process.exit(0); };
@@ -691,23 +690,8 @@ function cmdStart(args) {
691
690
  process.on('SIGBREAK', naoAbsorver);
692
691
  }
693
692
 
694
- // Keep-alive (regra inviolável do AGENTS.md): mantém o event loop ativo
695
- // mesmo sem I/O de console (MobaXterm minimizado / sem stream ativo).
696
693
  setInterval(() => {}, 1000);
697
-
698
- // Terminal preso (regra inviolável do AGENTS.md) — SOLUÇÃO DEFINITIVA:
699
- // ao retomar a entrada padrão IMEDIATAMENTE antes das mensagens finais
700
- // de sucesso, o Node permanece vivo indefinidamente aguardando qualquer
701
- // digitação do usuário. O prompt do MobaXterm NUNCA é devolvido e a PTY
702
- // (e com ela o uvicorn) não é fechada. Esta linha NÃO depende de timers
703
- // nem de npx — NUNCA REMOVER.
704
694
  process.stdin.resume();
705
-
706
- // Rede de segurança (regra inviolável do AGENTS.md): se o event loop
707
- // esvaziar (stdin em EOF, timer perdido, qualquer surpresa do terminal/
708
- // PTY do MobaXterm), o Node re-ativa o keep-alive no 'beforeExit' em vez
709
- // de encerrar — o prompt NUNCA é devolvido e o uvicorn não fica órfão.
710
- // Erros no stream de stdin também nunca derrubam o launcher.
711
695
  process.stdin.on('error', () => {});
712
696
  process.on('beforeExit', () => {
713
697
  setInterval(() => {}, 1000);
@@ -721,6 +705,12 @@ function cmdStart(args) {
721
705
  ? 'ℹ Para encerrar: Ctrl+Break ou feche a janela (Ctrl+C é absorvido no Windows contra sinais espúrios do MobaXterm).'
722
706
  : 'ℹ Para encerrar: Ctrl+C.',
723
707
  );
708
+
709
+ // Congela o fluxo principal PARA SEMPRE (regra inviolável): a promessa
710
+ // nunca resolve, então nenhum código posterior pode rodar e nenhum exit
711
+ // "natural" acontece. A saída acontece somente pelo ciclo de vida do
712
+ // filho (Python morre) ou por SIGTERM/SIGBREAK — ambos com log.
713
+ await new Promise(() => {});
724
714
  })().catch((err) => {
725
715
  console.error('✗ Erro inesperado ao iniciar:', err && err.message ? err.message : err);
726
716
  process.exit(1);