@danhachuel/leadmaster 0.1.16 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/backend/__pycache__/main.cpython-311.pyc +0 -0
- package/backend/services/__pycache__/__init__.cpython-311.pyc +0 -0
- package/backend/services/__pycache__/agents.cpython-311.pyc +0 -0
- package/backend/services/__pycache__/crm_agent.cpython-311.pyc +0 -0
- package/backend/services/__pycache__/extractors.cpython-311.pyc +0 -0
- package/backend/services/__pycache__/flags.cpython-311.pyc +0 -0
- package/backend/services/__pycache__/llm.cpython-311.pyc +0 -0
- package/backend/services/__pycache__/storage.cpython-311.pyc +0 -0
- package/backend/tests/__pycache__/conftest.cpython-311-pytest-8.4.2.pyc +0 -0
- package/backend/tests/__pycache__/test_campaigns_routes.cpython-311-pytest-8.4.2.pyc +0 -0
- package/backend/tests/__pycache__/test_cnpj_extractor.cpython-311-pytest-8.4.2.pyc +0 -0
- package/backend/tests/__pycache__/test_crm_routes.cpython-311-pytest-8.4.2.pyc +0 -0
- package/backend/tests/__pycache__/test_instagram_extractor.cpython-311-pytest-8.4.2.pyc +0 -0
- package/backend/tests/__pycache__/test_launcher_protections.cpython-311-pytest-8.4.2.pyc +0 -0
- package/backend/tests/__pycache__/test_maps_extractor.cpython-311-pytest-8.4.2.pyc +0 -0
- package/backend/tests/__pycache__/test_process_flags.cpython-311-pytest-8.4.2.pyc +0 -0
- package/backend/tests/test_launcher_protections.py +13 -0
- package/frontend/dist/assets/{index-BTNs0Fhp.js → index-BWyDEt5a.js} +1 -1
- package/frontend/dist/index.html +1 -1
- package/package.json +1 -1
- package/scripts/cli.mjs +66 -5
package/CHANGELOG.md
CHANGED
|
@@ -37,6 +37,31 @@ 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.17] — 2026-11-09
|
|
41
|
+
|
|
42
|
+
### Corrigido
|
|
43
|
+
|
|
44
|
+
- **Erro `EBUSY` do npx — migração para Runtime Permanente.** A 0.1.15 tentou
|
|
45
|
+
resolver com kill switch dentro do launcher, o que é impossível: o `EBUSY`
|
|
46
|
+
acontece no gerenciador de arquivos do `npx` (`_npx\...`) **antes** de o
|
|
47
|
+
`cli.mjs` ser carregado, então o kill switch nunca rodava. Causa raiz: o
|
|
48
|
+
`uvicorn` era spawnado com `cwd` dentro do cache do npx, segurando o
|
|
49
|
+
diretório. Nova arquitetura:
|
|
50
|
+
- Pasta permanente **`<home>/.runtime`** guarda `backend/` + `frontend/dist/`.
|
|
51
|
+
- `install`: kill switch (libera arquivos) → apaga o `.runtime` anterior →
|
|
52
|
+
copia esta versão do pacote para ele. O cache do npx nunca mais é tocado
|
|
53
|
+
por processos nossos.
|
|
54
|
+
- `start`: spawna o Python a partir de **`<home>/.runtime/backend`** (nunca
|
|
55
|
+
de `__dirname`); sem `.runtime` pronto, auto-aprovisiona; com pronto,
|
|
56
|
+
reutiliza sem recopiar.
|
|
57
|
+
- Launcher faz `process.chdir(home)` na entrada — cwd fora do cache.
|
|
58
|
+
- Regra inviolável documentada no AGENTS.md ("terminantemente proibido
|
|
59
|
+
executar o servidor a partir da pasta temporária do npx") + gate de
|
|
60
|
+
regressão (`.runtime`, `copiarParaRuntime`, `cpSync`, `rmSync`,
|
|
61
|
+
`path.join(runtime, 'backend')`, `process.chdir(homeLauncher)`).
|
|
62
|
+
- `.runtime/` adicionado ao `.gitignore`; `doctor` reporta o status do
|
|
63
|
+
runtime.
|
|
64
|
+
|
|
40
65
|
## [0.1.16] — 2026-11-09
|
|
41
66
|
|
|
42
67
|
### Adicionado
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -78,6 +78,19 @@ class TestLauncherProtections:
|
|
|
78
78
|
assert "já está rodando" not in _conteudo().lower()
|
|
79
79
|
assert "ja esta rodando" not in _conteudo().lower()
|
|
80
80
|
|
|
81
|
+
def test_runtime_permanente(self):
|
|
82
|
+
# Arquitetura anti-EBUSY: executar de <home>/.runtime, nunca do cache npx.
|
|
83
|
+
c = _conteudo()
|
|
84
|
+
assert ".runtime" in c
|
|
85
|
+
assert "copiarParaRuntime" in c
|
|
86
|
+
assert "runtimeDir" in c
|
|
87
|
+
assert "cpSync" in c # cópia recursiva para o runtime
|
|
88
|
+
assert "rmSync" in c # limpeza do runtime anterior
|
|
89
|
+
# O spawn do servidor usa o runtime, não o PKG_ROOT.
|
|
90
|
+
assert "path.join(runtime, 'backend')" in c
|
|
91
|
+
# O launcher sai do cache do npx logo na entrada.
|
|
92
|
+
assert "process.chdir(homeLauncher)" in c
|
|
93
|
+
|
|
81
94
|
def test_sigterm_nao_absorvido(self):
|
|
82
95
|
# SIGTERM/SIGBREAK são paradas legítimas: sempre encerram.
|
|
83
96
|
c = _conteudo()
|
|
@@ -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 Al(){return Al=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},Al.apply(null,arguments)}function py(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 WS(t){return!!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)}function HS(t,e){return t.button===0&&(!e||e==="_self")&&!WS(t)}function Rd(t){return t===void 0&&(t=""),new URLSearchParams(typeof t=="string"||Array.isArray(t)||t instanceof URLSearchParams?t:Object.keys(t).reduce((e,n)=>{let s=t[n];return e.concat(Array.isArray(s)?s.map(r=>[n,r]):[[n,s]])},[]))}function VS(t,e){let n=Rd(t);return e&&e.forEach((s,r)=>{n.has(r)||e.getAll(r).forEach(i=>{n.append(r,i)})}),n}const QS=["onClick","relative","reloadDocument","replace","state","target","to","preventScrollReset","viewTransition"],KS=["aria-current","caseSensitive","className","end","style","to","viewTransition","children"],YS="6";try{window.__reactRouterVersion=YS}catch{}const XS=S.createContext({isTransitioning:!1}),qS="startTransition",Qp=ib[qS];function GS(t){let{basename:e,children:n,future:s,window:r}=t,i=S.useRef();i.current==null&&(i.current=tS({window:r,v5Compat:!0}));let o=i.current,[a,l]=S.useState({action:o.action,location:o.location}),{v7_startTransition:c}=s||{},d=S.useCallback(f=>{c&&Qp?Qp(()=>l(f)):l(f)},[l,c]);return S.useLayoutEffect(()=>o.listen(d),[o,d]),S.useEffect(()=>BS(s),[s]),S.createElement($S,{basename:e,children:n,location:a.location,navigationType:a.action,navigator:o,future:s})}const JS=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",ZS=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,e_=S.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=py(e,QS),{basename:p}=S.useContext(Rs),m,g=!1;if(typeof c=="string"&&ZS.test(c)&&(m=c,JS))try{let v=new URL(window.location.href),_=c.startsWith("//")?new URL(v.protocol+c):new URL(c),w=pi(_.pathname,p);_.origin===v.origin&&w!=null?c=w+_.search+_.hash:g=!0}catch{}let b=CS(c,{relative:r}),y=n_(c,{replace:o,state:a,target:l,preventScrollReset:d,relative:r,viewTransition:f});function x(v){s&&s(v),v.defaultPrevented||y(v)}return S.createElement("a",Al({},h,{href:m||b,onClick:g||i?s:x,ref:n,target:l}))}),gy=S.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=py(e,KS),h=vc(l,{relative:f.relative}),p=Os(),m=S.useContext(ly),{navigator:g,basename:b}=S.useContext(Rs),y=m!=null&&r_(h)&&c===!0,x=g.encodeLocation?g.encodeLocation(h).pathname:h.pathname,v=p.pathname,_=m&&m.navigation&&m.navigation.location?m.navigation.location.pathname:null;r||(v=v.toLowerCase(),_=_?_.toLowerCase():null,x=x.toLowerCase()),_&&b&&(_=pi(_,b)||_);const w=x!=="/"&&x.endsWith("/")?x.length-1:x.length;let k=v===x||!o&&v.startsWith(x)&&v.charAt(w)==="/",C=_!=null&&(_===x||!o&&_.startsWith(x)&&_.charAt(x.length)==="/"),j={isActive:k,isPending:C,isTransitioning:y},M=k?s:void 0,E;typeof i=="function"?E=i(j):E=[i,k?"active":null,C?"pending":null,y?"transitioning":null].filter(Boolean).join(" ");let P=typeof a=="function"?a(j):a;return S.createElement(e_,Al({},f,{"aria-current":M,className:E,ref:n,style:P,to:l,viewTransition:c}),typeof d=="function"?d(j):d)});var Od;(function(t){t.UseScrollRestoration="useScrollRestoration",t.UseSubmit="useSubmit",t.UseSubmitFetcher="useSubmitFetcher",t.UseFetcher="useFetcher",t.useViewTransitionState="useViewTransitionState"})(Od||(Od={}));var Kp;(function(t){t.UseFetcher="useFetcher",t.UseFetchers="useFetchers",t.UseScrollRestoration="useScrollRestoration"})(Kp||(Kp={}));function t_(t){let e=S.useContext(xc);return e||je(!1),e}function n_(t,e){let{target:n,replace:s,state:r,preventScrollReset:i,relative:o,viewTransition:a}=e===void 0?{}:e,l=bi(),c=Os(),d=vc(t,{relative:o});return S.useCallback(f=>{if(HS(f,n)){f.preventDefault();let h=s!==void 0?s:Ll(c)===Ll(d);l(t,{replace:h,state:r,preventScrollReset:i,relative:o,viewTransition:a})}},[c,l,d,s,r,n,t,i,o,a])}function s_(t){let e=S.useRef(Rd(t)),n=S.useRef(!1),s=Os(),r=S.useMemo(()=>VS(s.search,n.current?null:e.current),[s.search]),i=bi(),o=S.useCallback((a,l)=>{const c=Rd(typeof a=="function"?a(r):a);n.current=!0,i("?"+c,l)},[i,r]);return[r,o]}function r_(t,e){e===void 0&&(e={});let n=S.useContext(XS);n==null&&je(!1);let{basename:s}=t_(Od.useViewTransitionState),r=vc(t,{relative:e.relative});if(!n.isTransitioning)return!1;let i=pi(n.currentLocation.pathname,s)||n.currentLocation.pathname,o=pi(n.nextLocation.pathname,s)||n.nextLocation.pathname;return Md(r.pathname,o)!=null||Md(r.pathname,i)!=null}const i_="/api";class o_ extends Error{constructor(n,s){super(s);B(this,"status");this.status=n,this.name="ApiError"}}async function U(t,e={}){const n=await fetch(i_+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 o_(n.status,s)}if(n.status!==204)return n.json()}const Me=t=>({method:"POST",body:JSON.stringify(t)}),W={health:()=>U("/health"),doctor:()=>U("/doctor"),shutdown:()=>U("/shutdown",Me({})),metricas:()=>U("/metrics"),leads:{listar:()=>U("/leads"),criar:t=>U("/leads",Me(t)),atualizar:(t,e)=>U(`/leads/${t}`,{method:"PATCH",body:JSON.stringify(e)}),remover:t=>U(`/leads/${t}`,{method:"DELETE"}),importar:t=>U("/leads/import",Me({leads:t})),enriquecer:t=>U(`/leads/${t}/enrich`,Me({}))},crm:{leadsListar:()=>U("/crm/leads"),criarLead:t=>U("/crm/leads",Me(t)),moverLead:(t,e)=>U(`/crm/leads/${t}/etapa`,{method:"PUT",body:JSON.stringify({etapa:e})}),sugestoes:()=>U("/crm/sugestoes"),aplicar:(t,e)=>U("/crm/sugestoes/aplicar",Me({lead_id:t,para:e}))},campanhas:{listar:()=>U("/campaigns"),criar:t=>U("/campaigns",Me(t)),status:(t,e)=>U(`/campaigns/${t}/status`,{method:"PUT",body:JSON.stringify({status:e})}),iniciar:t=>U(`/campaigns/${t}/start`,Me({})),pausar:t=>U(`/campaigns/${t}/pause`,Me({})),parar:t=>U(`/campaigns/${t}/stop`,Me({})),remover:t=>U(`/campaigns/${t}`,{method:"DELETE"})},extrair:{maps:t=>U("/extractors/maps",Me(t)),instagram:(t,e)=>U("/extractors/instagram",Me({profile_url:t,limit:e})),cnpj:t=>U("/extract/cnpj",Me({cnpj:t})),cnpjBusca:t=>U("/extract/cnpj/search",Me(t)),cnpjExtract:t=>U("/extractors/cnpj",Me(t))},config:{obter:()=>U("/config"),salvar:t=>U("/config",{method:"PUT",body:JSON.stringify(t)}),testarLlm:()=>U("/config/test/llm",Me({})),testarIntegracao:t=>U("/config/test/integracao",Me({url:t}))},integracoes:()=>U("/integrations/status"),agentes:{templates:()=>U("/agents/templates"),salvarTemplates:t=>U("/agents/templates",{method:"PUT",body:JSON.stringify(t)}),testarTemplate:(t,e)=>U("/agents/templates/test",Me({template:t,lead_id:e})),chat:t=>U("/agents/attendance/chat",Me({mensagens:t}))},webhook:{obter:()=>U("/webhook"),salvar:t=>U("/webhook",{method:"PUT",body:JSON.stringify(t)}),testar:t=>U("/webhook/test",Me({url:t}))}};function H(...t){return t.filter(Boolean).join(" ")}function a_(t){return(t||"?").trim().charAt(0).toUpperCase()||"?"}function Il(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 na(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 l_(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 c_(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 u_(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 d_(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 f_={md:"max-w-lg",lg:"max-w-2xl",xl:"max-w-4xl"};function Xt({aberto:t,onFechar:e,titulo:n,children:s,icone:r="fas fa-circle-info",largura:i="md"}){return S.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",f_[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 h_=[{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 p_({titulo:t,nomeUsuario:e}){const[n,s]=S.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:a_(e)}),u.jsx("span",{className:"text-sm font-medium text-gray-700",children:e})]}),u.jsxs(Xt,{aberto:n,onFechar:()=>s(!1),titulo:"Tutorial rápido",icone:"fas fa-graduation-cap",children:[u.jsx("ol",{className:"space-y-4",children:h_.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 g_=[{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"}]},{secao:"Integrações",itens:[{para:"/webhook",icone:"fas fa-link",rotulo:"Webhook"},{para:"/suporte",icone:"fas fa-headset",rotulo:"Suporte"}]}];function m_({onSair:t}){const e=Os(),[n,s]=S.useState(!1);S.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.16"]})]})}),u.jsx("nav",{className:"flex-1 overflow-y-auto py-4",children:u.jsx("ul",{className:"space-y-1",children:g_.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(Yp,{para:"/agentes?tab=disparo",rotulo:"Agente de Disparo",ativo:e.pathname==="/agentes"&&e.search.includes("disparo")})}),u.jsx("li",{children:u.jsx(Yp,{para:"/agentes?tab=atendimento",rotulo:"Agente de Atendimento",ativo:e.pathname==="/agentes"&&e.search.includes("atendimento")})})]})]},"agentes"):u.jsx("li",{children:u.jsxs(gy,{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 Yp({para:t,rotulo:e,ativo:n}){return u.jsx(gy,{to:t,className:H("sidebar-link block px-4 py-2 text-xs text-gray-300 rounded",n&&"active"),children:e})}const my=S.createContext({toast:()=>{}});function en(){return S.useContext(my)}const x_={sucesso:"bg-green-600",erro:"bg-red-600",info:"bg-indigo-600"},y_={sucesso:"fas fa-check-circle",erro:"fas fa-exclamation-circle",info:"fas fa-info-circle"};function v_({children:t}){const[e,n]=S.useState([]),s=S.useRef(0),r=S.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(my.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(x_[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(y_[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 Pa({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 Al(){return Al=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},Al.apply(null,arguments)}function py(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 WS(t){return!!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)}function HS(t,e){return t.button===0&&(!e||e==="_self")&&!WS(t)}function Rd(t){return t===void 0&&(t=""),new URLSearchParams(typeof t=="string"||Array.isArray(t)||t instanceof URLSearchParams?t:Object.keys(t).reduce((e,n)=>{let s=t[n];return e.concat(Array.isArray(s)?s.map(r=>[n,r]):[[n,s]])},[]))}function VS(t,e){let n=Rd(t);return e&&e.forEach((s,r)=>{n.has(r)||e.getAll(r).forEach(i=>{n.append(r,i)})}),n}const QS=["onClick","relative","reloadDocument","replace","state","target","to","preventScrollReset","viewTransition"],KS=["aria-current","caseSensitive","className","end","style","to","viewTransition","children"],YS="6";try{window.__reactRouterVersion=YS}catch{}const XS=S.createContext({isTransitioning:!1}),qS="startTransition",Qp=ib[qS];function GS(t){let{basename:e,children:n,future:s,window:r}=t,i=S.useRef();i.current==null&&(i.current=tS({window:r,v5Compat:!0}));let o=i.current,[a,l]=S.useState({action:o.action,location:o.location}),{v7_startTransition:c}=s||{},d=S.useCallback(f=>{c&&Qp?Qp(()=>l(f)):l(f)},[l,c]);return S.useLayoutEffect(()=>o.listen(d),[o,d]),S.useEffect(()=>BS(s),[s]),S.createElement($S,{basename:e,children:n,location:a.location,navigationType:a.action,navigator:o,future:s})}const JS=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",ZS=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,e_=S.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=py(e,QS),{basename:p}=S.useContext(Rs),m,g=!1;if(typeof c=="string"&&ZS.test(c)&&(m=c,JS))try{let v=new URL(window.location.href),_=c.startsWith("//")?new URL(v.protocol+c):new URL(c),w=pi(_.pathname,p);_.origin===v.origin&&w!=null?c=w+_.search+_.hash:g=!0}catch{}let b=CS(c,{relative:r}),y=n_(c,{replace:o,state:a,target:l,preventScrollReset:d,relative:r,viewTransition:f});function x(v){s&&s(v),v.defaultPrevented||y(v)}return S.createElement("a",Al({},h,{href:m||b,onClick:g||i?s:x,ref:n,target:l}))}),gy=S.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=py(e,KS),h=vc(l,{relative:f.relative}),p=Os(),m=S.useContext(ly),{navigator:g,basename:b}=S.useContext(Rs),y=m!=null&&r_(h)&&c===!0,x=g.encodeLocation?g.encodeLocation(h).pathname:h.pathname,v=p.pathname,_=m&&m.navigation&&m.navigation.location?m.navigation.location.pathname:null;r||(v=v.toLowerCase(),_=_?_.toLowerCase():null,x=x.toLowerCase()),_&&b&&(_=pi(_,b)||_);const w=x!=="/"&&x.endsWith("/")?x.length-1:x.length;let k=v===x||!o&&v.startsWith(x)&&v.charAt(w)==="/",C=_!=null&&(_===x||!o&&_.startsWith(x)&&_.charAt(x.length)==="/"),j={isActive:k,isPending:C,isTransitioning:y},M=k?s:void 0,E;typeof i=="function"?E=i(j):E=[i,k?"active":null,C?"pending":null,y?"transitioning":null].filter(Boolean).join(" ");let P=typeof a=="function"?a(j):a;return S.createElement(e_,Al({},f,{"aria-current":M,className:E,ref:n,style:P,to:l,viewTransition:c}),typeof d=="function"?d(j):d)});var Od;(function(t){t.UseScrollRestoration="useScrollRestoration",t.UseSubmit="useSubmit",t.UseSubmitFetcher="useSubmitFetcher",t.UseFetcher="useFetcher",t.useViewTransitionState="useViewTransitionState"})(Od||(Od={}));var Kp;(function(t){t.UseFetcher="useFetcher",t.UseFetchers="useFetchers",t.UseScrollRestoration="useScrollRestoration"})(Kp||(Kp={}));function t_(t){let e=S.useContext(xc);return e||je(!1),e}function n_(t,e){let{target:n,replace:s,state:r,preventScrollReset:i,relative:o,viewTransition:a}=e===void 0?{}:e,l=bi(),c=Os(),d=vc(t,{relative:o});return S.useCallback(f=>{if(HS(f,n)){f.preventDefault();let h=s!==void 0?s:Ll(c)===Ll(d);l(t,{replace:h,state:r,preventScrollReset:i,relative:o,viewTransition:a})}},[c,l,d,s,r,n,t,i,o,a])}function s_(t){let e=S.useRef(Rd(t)),n=S.useRef(!1),s=Os(),r=S.useMemo(()=>VS(s.search,n.current?null:e.current),[s.search]),i=bi(),o=S.useCallback((a,l)=>{const c=Rd(typeof a=="function"?a(r):a);n.current=!0,i("?"+c,l)},[i,r]);return[r,o]}function r_(t,e){e===void 0&&(e={});let n=S.useContext(XS);n==null&&je(!1);let{basename:s}=t_(Od.useViewTransitionState),r=vc(t,{relative:e.relative});if(!n.isTransitioning)return!1;let i=pi(n.currentLocation.pathname,s)||n.currentLocation.pathname,o=pi(n.nextLocation.pathname,s)||n.nextLocation.pathname;return Md(r.pathname,o)!=null||Md(r.pathname,i)!=null}const i_="/api";class o_ extends Error{constructor(n,s){super(s);B(this,"status");this.status=n,this.name="ApiError"}}async function U(t,e={}){const n=await fetch(i_+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 o_(n.status,s)}if(n.status!==204)return n.json()}const Me=t=>({method:"POST",body:JSON.stringify(t)}),W={health:()=>U("/health"),doctor:()=>U("/doctor"),shutdown:()=>U("/shutdown",Me({})),metricas:()=>U("/metrics"),leads:{listar:()=>U("/leads"),criar:t=>U("/leads",Me(t)),atualizar:(t,e)=>U(`/leads/${t}`,{method:"PATCH",body:JSON.stringify(e)}),remover:t=>U(`/leads/${t}`,{method:"DELETE"}),importar:t=>U("/leads/import",Me({leads:t})),enriquecer:t=>U(`/leads/${t}/enrich`,Me({}))},crm:{leadsListar:()=>U("/crm/leads"),criarLead:t=>U("/crm/leads",Me(t)),moverLead:(t,e)=>U(`/crm/leads/${t}/etapa`,{method:"PUT",body:JSON.stringify({etapa:e})}),sugestoes:()=>U("/crm/sugestoes"),aplicar:(t,e)=>U("/crm/sugestoes/aplicar",Me({lead_id:t,para:e}))},campanhas:{listar:()=>U("/campaigns"),criar:t=>U("/campaigns",Me(t)),status:(t,e)=>U(`/campaigns/${t}/status`,{method:"PUT",body:JSON.stringify({status:e})}),iniciar:t=>U(`/campaigns/${t}/start`,Me({})),pausar:t=>U(`/campaigns/${t}/pause`,Me({})),parar:t=>U(`/campaigns/${t}/stop`,Me({})),remover:t=>U(`/campaigns/${t}`,{method:"DELETE"})},extrair:{maps:t=>U("/extractors/maps",Me(t)),instagram:(t,e)=>U("/extractors/instagram",Me({profile_url:t,limit:e})),cnpj:t=>U("/extract/cnpj",Me({cnpj:t})),cnpjBusca:t=>U("/extract/cnpj/search",Me(t)),cnpjExtract:t=>U("/extractors/cnpj",Me(t))},config:{obter:()=>U("/config"),salvar:t=>U("/config",{method:"PUT",body:JSON.stringify(t)}),testarLlm:()=>U("/config/test/llm",Me({})),testarIntegracao:t=>U("/config/test/integracao",Me({url:t}))},integracoes:()=>U("/integrations/status"),agentes:{templates:()=>U("/agents/templates"),salvarTemplates:t=>U("/agents/templates",{method:"PUT",body:JSON.stringify(t)}),testarTemplate:(t,e)=>U("/agents/templates/test",Me({template:t,lead_id:e})),chat:t=>U("/agents/attendance/chat",Me({mensagens:t}))},webhook:{obter:()=>U("/webhook"),salvar:t=>U("/webhook",{method:"PUT",body:JSON.stringify(t)}),testar:t=>U("/webhook/test",Me({url:t}))}};function H(...t){return t.filter(Boolean).join(" ")}function a_(t){return(t||"?").trim().charAt(0).toUpperCase()||"?"}function Il(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 na(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 l_(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 c_(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 u_(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 d_(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 f_={md:"max-w-lg",lg:"max-w-2xl",xl:"max-w-4xl"};function Xt({aberto:t,onFechar:e,titulo:n,children:s,icone:r="fas fa-circle-info",largura:i="md"}){return S.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",f_[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 h_=[{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 p_({titulo:t,nomeUsuario:e}){const[n,s]=S.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:a_(e)}),u.jsx("span",{className:"text-sm font-medium text-gray-700",children:e})]}),u.jsxs(Xt,{aberto:n,onFechar:()=>s(!1),titulo:"Tutorial rápido",icone:"fas fa-graduation-cap",children:[u.jsx("ol",{className:"space-y-4",children:h_.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 g_=[{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"}]},{secao:"Integrações",itens:[{para:"/webhook",icone:"fas fa-link",rotulo:"Webhook"},{para:"/suporte",icone:"fas fa-headset",rotulo:"Suporte"}]}];function m_({onSair:t}){const e=Os(),[n,s]=S.useState(!1);S.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.17"]})]})}),u.jsx("nav",{className:"flex-1 overflow-y-auto py-4",children:u.jsx("ul",{className:"space-y-1",children:g_.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(Yp,{para:"/agentes?tab=disparo",rotulo:"Agente de Disparo",ativo:e.pathname==="/agentes"&&e.search.includes("disparo")})}),u.jsx("li",{children:u.jsx(Yp,{para:"/agentes?tab=atendimento",rotulo:"Agente de Atendimento",ativo:e.pathname==="/agentes"&&e.search.includes("atendimento")})})]})]},"agentes"):u.jsx("li",{children:u.jsxs(gy,{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 Yp({para:t,rotulo:e,ativo:n}){return u.jsx(gy,{to:t,className:H("sidebar-link block px-4 py-2 text-xs text-gray-300 rounded",n&&"active"),children:e})}const my=S.createContext({toast:()=>{}});function en(){return S.useContext(my)}const x_={sucesso:"bg-green-600",erro:"bg-red-600",info:"bg-indigo-600"},y_={sucesso:"fas fa-check-circle",erro:"fas fa-exclamation-circle",info:"fas fa-info-circle"};function v_({children:t}){const[e,n]=S.useState([]),s=S.useRef(0),r=S.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(my.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(x_[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(y_[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 Pa({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
|
package/frontend/dist/index.html
CHANGED
|
@@ -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-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-BWyDEt5a.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/assets/index-BKo_OphM.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body class="bg-gray-100 min-h-screen">
|
package/package.json
CHANGED
package/scripts/cli.mjs
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import { spawn, spawnSync, execSync } from 'node:child_process';
|
|
26
|
-
import { existsSync, mkdirSync, readFileSync, appendFileSync } from 'node:fs';
|
|
26
|
+
import { existsSync, mkdirSync, readFileSync, appendFileSync, rmSync, cpSync } from 'node:fs';
|
|
27
27
|
import { homedir, platform } from 'node:os';
|
|
28
28
|
import net from 'node:net';
|
|
29
29
|
import path from 'node:path';
|
|
@@ -87,6 +87,41 @@ function ensureHome(home) {
|
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// Runtime permanente (regra inviolável do AGENTS.md)
|
|
92
|
+
//
|
|
93
|
+
// O servidor NUNCA executa a partir do cache do npx (`_npx\...`): um processo
|
|
94
|
+
// rodando de lá segura o diretório e o npx falha com EBUSY na próxima
|
|
95
|
+
// instalação — antes mesmo de qualquer código do launcher rodar (por isso o
|
|
96
|
+
// kill switch sozinho não resolvia). Solução: `install` copia backend/ e
|
|
97
|
+
// frontend/dist/ para <home>/.runtime e `start` executa de lá. O cache do npx
|
|
98
|
+
// nunca é tocado por processos nossos.
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
function runtimeDir(home) {
|
|
102
|
+
return path.join(home, '.runtime');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function runtimePronto(home) {
|
|
106
|
+
return existsSync(path.join(runtimeDir(home), 'backend', 'main.py'));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Copia backend/ e frontend/dist/ do pacote para o runtime permanente. */
|
|
110
|
+
function copiarParaRuntime(home) {
|
|
111
|
+
const runtime = runtimeDir(home);
|
|
112
|
+
rmSync(runtime, { recursive: true, force: true });
|
|
113
|
+
mkdirSync(runtime, { recursive: true });
|
|
114
|
+
cpSync(path.join(PKG_ROOT, 'backend'), path.join(runtime, 'backend'), {
|
|
115
|
+
recursive: true,
|
|
116
|
+
filter: (src) => !src.includes('__pycache__') && !src.endsWith('.pyc') && !src.includes(`${path.sep}.venv`),
|
|
117
|
+
});
|
|
118
|
+
const dist = path.join(PKG_ROOT, 'frontend', 'dist');
|
|
119
|
+
if (existsSync(path.join(dist, 'index.html'))) {
|
|
120
|
+
cpSync(dist, path.join(runtime, 'frontend', 'dist'), { recursive: true });
|
|
121
|
+
}
|
|
122
|
+
return runtime;
|
|
123
|
+
}
|
|
124
|
+
|
|
90
125
|
// ---------------------------------------------------------------------------
|
|
91
126
|
// Python
|
|
92
127
|
// ---------------------------------------------------------------------------
|
|
@@ -471,6 +506,14 @@ async function cmdInstall(args) {
|
|
|
471
506
|
// Frontend (modo repositório): idempotente, só instala o que faltar.
|
|
472
507
|
instalarModoFonte();
|
|
473
508
|
|
|
509
|
+
// Runtime permanente: reutiliza a porta inicial já validada pelo kill switch
|
|
510
|
+
// acima, apaga o .runtime anterior (arquivos liberados) e copia esta versão
|
|
511
|
+
// do pacote para lá. Daqui em diante o servidor NUNCA roda de dentro do
|
|
512
|
+
// cache do npx — o EBUSY acaba.
|
|
513
|
+
await encerrarInstanciaAnterior(portaInicial);
|
|
514
|
+
const runtime = copiarParaRuntime(home);
|
|
515
|
+
console.log(`✓ Runtime permanente preparado: ${runtime}`);
|
|
516
|
+
|
|
474
517
|
console.log('');
|
|
475
518
|
console.log('Instalação concluída. Para usar (comando único):');
|
|
476
519
|
console.log(' npx --yes @danhachuel/leadmaster@latest');
|
|
@@ -524,20 +567,28 @@ function cmdStart(args) {
|
|
|
524
567
|
|
|
525
568
|
// Comando único (estilo Thunderbolt): o start também se auto-prepara.
|
|
526
569
|
// Modo fonte sem deps/build? Instala só o que faltar antes de subir.
|
|
527
|
-
// Pacote npx: UI já vem pronta (dist) e nada disto roda.
|
|
528
570
|
if (existsSync(path.join(PKG_ROOT, 'frontend', 'package.json'))) {
|
|
529
571
|
instalarModoFonte();
|
|
530
|
-
} else if (!existsSync(path.join(PKG_ROOT, 'frontend', 'dist', 'index.html'))) {
|
|
531
|
-
console.warn('⚠ Interface construída não encontrada (frontend/dist).');
|
|
532
572
|
}
|
|
533
573
|
|
|
574
|
+
// Kill switch: se uma instância ANTIGA (runtime anterior) ainda ocupar a
|
|
575
|
+
// porta, ela é encerrada antes da nova assumir — nunca reutilizamos.
|
|
576
|
+
await encerrarInstanciaAnterior(portaInicial);
|
|
577
|
+
|
|
578
|
+
// Runtime permanente (regra inviolável): o servidor NUNCA roda de dentro
|
|
579
|
+
// do cache do npx (EBUSY na próxima instalação). Sem runtime pronto, o
|
|
580
|
+
// start se auto-aprovisiona copiando para <home>/.runtime; com runtime
|
|
581
|
+
// pronto, reutiliza (sem recopiar nada).
|
|
582
|
+
const runtime = runtimePronto(home) ? runtimeDir(home) : copiarParaRuntime(home);
|
|
583
|
+
console.log(`✓ Executando a partir do runtime permanente: ${runtime}`);
|
|
584
|
+
|
|
534
585
|
// Workers nascem em grupo de processos próprio (detached: true =
|
|
535
586
|
// CREATE_NEW_PROCESS_GROUP no Windows): sinais de Ctrl+C do terminal
|
|
536
587
|
// (MobaXterm, VS Code, perda de foco) matam apenas o launcher — o
|
|
537
588
|
// servidor Python continua rodando e só é encerrado pelo kill()
|
|
538
589
|
// controlado abaixo (ou sobrevive para a próxima execução detectar).
|
|
539
590
|
const child = spawn(python.cmd, [...python.args, '-m', 'uvicorn', 'main:app', '--host', '127.0.0.1', '--port', String(porta), '--no-access-log'], {
|
|
540
|
-
cwd: path.join(
|
|
591
|
+
cwd: path.join(runtime, 'backend'),
|
|
541
592
|
env: {
|
|
542
593
|
...process.env,
|
|
543
594
|
LEADMASTER_HOME: home,
|
|
@@ -674,6 +725,7 @@ async function cmdDoctor(args) {
|
|
|
674
725
|
console.log(`Servidor UI ........ ${ativaEm ? `rodando em http://127.0.0.1:${ativaEm}` : `parado (porta inicial ${portaInicial})`}`);
|
|
675
726
|
const hasDist = existsSync(path.join(PKG_ROOT, 'frontend', 'dist', 'index.html'));
|
|
676
727
|
console.log(`Interface (dist) ... ${hasDist ? 'presente' : 'ausente (modo dev ou rebuild necessário)'}`);
|
|
728
|
+
console.log(`Runtime permanente . ${runtimePronto(home) ? `pronto (${runtimeDir(home)})` : 'ausente — rode install'}`);
|
|
677
729
|
console.log('');
|
|
678
730
|
console.log('Diagnóstico concluído.');
|
|
679
731
|
}
|
|
@@ -721,6 +773,15 @@ mkdirSync(LOG_DIR, { recursive: true });
|
|
|
721
773
|
const CONSOLE_LOG = path.join(LOG_DIR, 'launcher_console.log');
|
|
722
774
|
[process.stdout, process.stderr].forEach((stream) => guardConsoleStream(stream, CONSOLE_LOG));
|
|
723
775
|
|
|
776
|
+
// Nunca manter o cwd dentro do cache do npx: o launcher nasce com o cwd de
|
|
777
|
+
// onde o usuário rodou o comando; assim que sabemos o home, saímos de lá —
|
|
778
|
+
// nenhum processo nosso segura diretório do cache (fim do EBUSY no npx).
|
|
779
|
+
try {
|
|
780
|
+
process.chdir(homeLauncher);
|
|
781
|
+
} catch {
|
|
782
|
+
/* melhor esforço */
|
|
783
|
+
}
|
|
784
|
+
|
|
724
785
|
switch (command) {
|
|
725
786
|
case 'install':
|
|
726
787
|
// cmdInstall é async (kill switch): a promise mantém o evento vivo até o
|