commit 18ae589d3aa6b2b1d0a5b6ecbe0ebcce3c3c7325 Author: WanderMotta Date: Tue Aug 11 19:00:04 2026 -0300 Redator de SD - preparado para deploy diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..683e0c6 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +# Fora do contexto de build (velocidade e segurança) +app/node_modules +app/data +app/.env +app/.env.example +*.zip +SD/ +prototipo-criador-de-demanda.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c36b21f --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +# Segredos e dados locais — NUNCA versionar +app/node_modules/ +app/.env +app/data/ + +# Arquivos soltos que não pertencem ao deploy +*.zip diff --git a/DEPLOY.md b/DEPLOY.md new file mode 100644 index 0000000..f631514 --- /dev/null +++ b/DEPLOY.md @@ -0,0 +1,89 @@ +# Deploy no Coolify — Redator de SD + +Método escolhido: **Dockerfile** (já pronto na raiz do projeto). É a melhor opção aqui porque +o projeto tem dois níveis — os arquivos de configuração do redator na raiz e o app em `app/` — +e o Nixpacks não lida bem com isso (detectaria o Node em `app/` e deixaria de fora +`regras-sd.json`, `schema-sd.json`, o prompt e a base). Docker Compose não acrescenta nada: +é um serviço só, e volumes/variáveis o painel do Coolify resolve. + +## Passo 0 — subir o projeto para um repositório Git PRIVADO + +O Coolify puxa o código de um repositório. Na pasta `D:\IasisSaude`: + +```bash +git init -b main +git add . +git commit -m "Redator de SD - preparado para deploy" +``` + +Crie um repositório **privado** no GitHub (ex.: `iasis/redator-sd`) e: + +```bash +git remote add origin https://github.com/SEU-USUARIO/redator-sd.git +git push -u origin main +``` + +O `.gitignore` da raiz já bloqueia `app/.env` (chave de API!), `app/data/` e `node_modules`. + +## Passo 1 — criar a aplicação no painel + +1. **Projects → seu projeto → + New → Application**. +2. Fonte: **Private Repository (with GitHub App)** → conecte a conta na primeira vez → + selecione o repositório e o branch `main`. +3. Em **Build Pack**, escolha **Dockerfile** (ele acha o `Dockerfile` na raiz sozinho). +4. Em **Ports Exposes**: `3344`. + +## Passo 2 — variáveis de ambiente (aba Environment Variables) + +| Variável | Valor | +|---|---| +| `ANTHROPIC_API_KEY` | a chave (marque como **secret**) | +| `APP_USUARIO` | usuário do login — **obrigatório na internet** | +| `APP_SENHA` | senha FORTE — é a única porta de entrada | +| `LLM_MODEL` | `claude-opus-5` (ou o padrão que preferir) | +| `LLM_MODELOS` | `claude-sonnet-5,claude-haiku-4-5-20251001` | +| `LLM_EFFORT` | `medium` | + +`PORT` não precisa: o padrão 3344 casa com o EXPOSE da imagem. + +## Passo 3 — persistência (aba Storages) — NÃO PULE + +Sem isto, **cada deploy apaga as conversas e o histórico da base**: + +1. **+ Add → Volume Mount** + - Destination Path: `/projeto/app/data` + - (guarda `sds.json`: conversas e rascunhos) +2. **+ Add → File Mount** + - Destination Path: `/projeto/base-conhecimento.json` + - Content: cole o conteúdo ATUAL do seu `D:\IasisSaude\base-conhecimento.json` + - (o app escreve neste arquivo ao enviar SDs e alimentar a base; o file mount + sobrevive aos deploys) + +⚠ A partir do primeiro uso em produção, a base **da VPS** vira a oficial — o arquivo +local no seu PC deixa de valer. Para trazer de volta, copie o conteúdo pela aba +Storages (ou `cat` no terminal do container). + +## Passo 4 — domínio e HTTPS + +1. No seu DNS, crie um registro **A** apontando (ex.: `redator.iasis.tech`) para o IP da VPS. +2. Na aplicação, em **Domains**: `https://redator.iasis.tech`. +3. O Coolify (Traefik) emite o certificado Let's Encrypt sozinho. + +## Passo 5 — health check (opcional, recomendado) + +Aba **Health Check**: Path `/api/sessao`, Port `3344` — responde 200 sem login. + +## Passo 6 — Deploy + +Botão **Deploy** e acompanhe os logs até aparecer: +`Redator de SD rodando em http://localhost:3344`. + +Atualizações futuras: `git add … && git commit && git push` — com **Auto Deploy** +ligado (padrão via webhook do GitHub App), o Coolify rebuilda sozinho. + +## Comportamentos esperados em produção + +- **Sessões em memória**: a cada deploy/restart, é preciso logar de novo. +- **Upload de PDF** (Base de Conhecimento): até 15 MB — passa pelo Traefik sem ajuste. +- **Editar prompt/regras/schema**: edite no repositório e faça push (vira deploy); + a base de conhecimento NÃO — ela vive no file mount e é editada pelo próprio app. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..10bd381 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +# Redator de SD — RMDS / SES-MG × Isis Saúde +# Imagem para deploy (no Coolify: Build Pack = Dockerfile, porta 3344). +# A estrutura do container espelha o projeto: arquivos de configuração do +# redator na raiz (/projeto) e o app em /projeto/app — igual ao disco local. +FROM node:22-alpine + +WORKDIR /projeto + +# Dependências primeiro, para aproveitar o cache entre builds +COPY app/package.json app/package-lock.json ./app/ +RUN cd app && npm ci --omit=dev + +# Arquivos de configuração do redator (fonte única — raiz do projeto). +# base-conhecimento.json entra como semente: em produção ele é sobreposto +# pelo file mount persistente do Coolify (senão cada deploy apagaria o histórico). +COPY prompt-redator-sd.md regras-sd.json schema-sd.json base-conhecimento.json ./ + +# Código do app (o .env NUNCA entra na imagem — variáveis vêm do painel do Coolify) +COPY app/src ./app/src +COPY app/public ./app/public + +ENV NODE_ENV=production +EXPOSE 3344 + +WORKDIR /projeto/app +CMD ["node", "src/server.js"] diff --git a/Manual-Redacao-SD.md b/Manual-Redacao-SD.md new file mode 100644 index 0000000..cc8f83e --- /dev/null +++ b/Manual-Redacao-SD.md @@ -0,0 +1,401 @@ +# Manual de Redação de Solicitação de Demanda (SD) + +## Como transformar uma demanda em entregáveis faturáveis, enquadrados e datados + +Contrato: Pregão Eletrônico nº 29/2025 — Processo 1321127 · SES-MG × Isis Saúde + +--- + +## A regra que organiza tudo + +**O entregável é a unidade faturável.** Ele carrega tipo, item, semanas, USTs e datas próprias. É por ele que se mede, se fatura e se controla saldo de pool. + +A fase — Descoberta, Design, Arquitetura, Construção — **não é um container**. É um atributo do entregável, e o que define seu time-box. + +``` +ENTREGÁVEL +├── Tipo (Descoberta / Design / Arquitetura / Construção) → define o time-box +├── Item do TR (I-02 / I-03 / I-04 / I-05) → define de qual pool debita +├── Semanas (2 a 4) → define a duração +├── USTs = time-box × semanas → o valor faturável +├── Período (data inicial a data final) → quando aconteceu +├── Artefatos comprobatórios → o que prova +└── Critérios de aceite → como se verifica +``` + +Duas consequências práticas: + +- **Uma SD pode ter dois entregáveis do mesmo tipo**, com escopos e datas diferentes. Não existe "a fase de Construção" — existem os entregáveis de Construção que a demanda exigiu. +- **Entregáveis de itens diferentes nunca se somam na mesma linha.** Cada um debita de um pool distinto e é medido separadamente. + +--- + +# PARTE I — MONTAGEM DOS ENTREGÁVEIS + +--- + +## Passo 1 — Entenda a demanda + +Três perguntas: + +**1. O que o cliente quer ver funcionando no final?** +Não o que precisa ser feito — o que ele quer *ver*. + +**2. Isso já existe ou é novo?** +Já existe e precisa mudar → manutenção. Nunca existiu → construção nova. + +**3. Qual é a natureza do trabalho?** +Construir um pedaço da plataforma? Automatizar um processo? Criar um dashboard? Treinar alguém? + +Demanda com mais de uma natureza cruza mais de um item. Isso é normal — vira entregáveis separados. + +--- + +## Passo 2 — Decomponha em atividades + +Blocos de trabalho que uma pessoa consiga explicar em uma frase. Não precisa ser granular. + +--- + +## Passo 3 — Enquadre cada atividade + +``` +1. É plataforma crescendo (módulo novo, fonte nova, conector novo)? → I-02 +2. É ensinar alguém a usar algo que já foi implantado? → I-03 +3. É automatizar tarefa repetitiva / corrigir algo em produção? → I-04 +4. É criar dashboard, algoritmo, IA, busca ativa, análise? → I-05 +``` + +**Teste da estrada** — para a dúvida clássica entre I-02 e I-04: + +> I-02 constrói a estrada. I-04 põe o piloto automático. +> Primeira vez que conecta ou constrói → I-02. +> Automatizar algo que já poderia ser feito manualmente → I-04. + +**Armadilhas frequentes:** + +| Situação | Item correto | Por quê | +|---|---|---| +| Produto novo sendo construído | I-02 | Ainda não existe para ser mantido | +| Dashboard, índice, score, ranking | I-05 | É inteligência, não automação | +| App em produção com bug de interface | I-04 | Manutenção perfectiva | +| Regra de negócio nova no motor | I-05 | Evolução de algoritmo | +| Oficina de discovery pré-implantação | I-05 | I-03 é consultoria pós-implantação | + +--- + +## Passo 4 — Agrupe as atividades em entregáveis + +Agrupe atividades que compartilham **o mesmo item e o mesmo tipo** e que produzem um artefato final coerente. + +| Tipo | O que entra | Pergunta-guia | +|---|---|---| +| **Descoberta** | Levantamento, mapeamento, investigação | "O que eu precisava saber antes de começar?" | +| **Design** | Visualização, UX, fluxos, protótipos, modelagem de regras | "Como vai parecer?" | +| **Arquitetura** | Planejamento técnico, modelo de dados, viabilidade | "Como vai ser construído?" | +| **Construção** | Implementação, teste, publicação | "Está funcionando?" | + +**Quando quebrar em mais de um entregável do mesmo tipo:** + +- O escopo excede 4 semanas → quebre, porque 4 é o teto por entregável +- Duas frentes rodam em paralelo com times distintos → entregáveis separados, mesmo período +- Um pedaço pode ser faturado antes do outro → separe, para não travar o faturamento do que já está pronto +- Atividades pertencem a itens diferentes → obrigatoriamente separados + +**Quando não quebrar:** + +- Só para "ter mais entregáveis". Cada entregável custa USTs próprias — quebrar infla o orçamento sem entregar mais +- Quando o resultado só faz sentido inteiro. Metade de um dashboard não é faturável + +Nem toda demanda tem os quatro tipos. Uma oficina pode ter só Descoberta e Construção. Uma manutenção, só Construção. Tipo inventado para "ficar completo" é glosa esperando acontecer. + +--- + +## Passo 5 — Defina semanas e datas de cada entregável + +``` +Mínimo: 2 semanas (regra contratual) +Máximo: 4 semanas +Típico: 2 a 3 semanas +``` + +| Se o entregável... | Semanas | +|---|---| +| É simples, escopo fechado, poucas dependências | 2 | +| É médio, precisa de reuniões e validação | 3 | +| É complexo, múltiplos stakeholders, muita incerteza | 4 | + +**Regras de data:** + +- Entregáveis **encadeados** (um depende do outro) não podem se sobrepor. Se Descoberta e Design compartilham semanas, os mesmos perfis aparecem alocados a 200% — é achado de auditoria. +- Entregáveis **paralelos** podem compartilhar período, desde que sejam times distintos. Declare isso explicitamente na SD, ou parecerá erro. +- A data final de cada entregável é a data que habilita seu faturamento. Ela precisa fazer sentido contra a evidência: entregável que fecha em 31/05 não pode ter como comprovação um documento enviado em 14/06. + +--- + +## Passo 6 — Calcule as USTs de cada entregável + +``` +USTs = time-box × semanas +``` + +**Time-box por tipo e item:** + +| | Descoberta | Design | Arquitetura | Construção | +|---|---|---|---|---| +| **I-02** | 40h | 40h | 40h | **40h** | +| **I-03** | 40h | 40h | 40h | **40h** | +| **I-04** | 40h | 40h | 40h | **80h** | +| **I-05** | 40h | 40h | 40h | **80h** | + +Descoberta, Design e Arquitetura são sempre 40h. A única diferença está na Construção: 40h em I-02/I-03, 80h em I-04/I-05. + +**USTs prontas para copiar:** + +| Semanas | Desc / Design / Arq (todos os itens) | Construção I-02 e I-03 | Construção I-04 e I-05 | +|---|---|---|---| +| 2 | 80 | 80 | **160** | +| 3 | 120 | 120 | **240** | +| 4 | 160 | 160 | **320** | + +--- + +## Passo 7 — Defina os artefatos comprobatórios de cada entregável + +**Entregável ≠ artefato.** O entregável é a unidade faturável. Os artefatos são os documentos que provam que ele aconteceu. + +Quanto maior o valor do entregável, mais lastro documental ele precisa carregar: + +| USTs do entregável | Artefatos comprobatórios mínimos | +|---|---| +| 80 | 1 | +| 120 a 160 | 2 | +| 240 ou mais | 3 | + +Nomeie artefatos que **existem de fato**. Documento comprobatório inventado é pior que entregável sem comprovação — o primeiro é problema de integridade, o segundo é só pendência. + +--- + +# PARTE II — REDAÇÃO DA SD + +--- + +## Cabeçalho + +| Campo | Regra de preenchimento | +|---|---| +| Cliente / Contratante | Secretaria de Estado de Saúde de Minas Gerais (SES-MG) | +| Contratada | Isis Saúde | +| Contrato | Pregão Eletrônico nº 29/2025 — Processo 1321127 | +| Projeto / Solução | Nome do projeto real desta demanda. **Confira** — é o campo mais copiado por engano | +| Nº da SD | Sequencial | +| Versão | V1, V2… | +| Data de abertura | DD/MM/AAAA. Nunca escreva "não formalizada" nem qualquer confissão de irregularidade | +| Período de execução | Da data inicial do primeiro entregável à data final do último | +| Item ou itens do TR | Número **e** título literal. Se a SD cruza itens, liste todos | +| Quantidade total de UST | Soma dos entregáveis, com número por extenso | +| Prazo de execução | Duração do calendário, não a soma das semanas quando há paralelismo | +| Quantidade de entregáveis | Número | + +--- + +## 1. Objetivo + +Dois a quatro períodos. Diz o que a SD formaliza e qual resultado permite alcançar. Não repete o contexto. + +--- + +## 2. Contexto e escopo da demanda + +Três a cinco parágrafos: + +1. **O problema de gestão** — o que trava, o que a SES-MG não consegue fazer hoje +2. **O insumo disponível** — o que já existe e por que não basta na forma atual +3. **O que a demanda entrega** — a solução em termos concretos +4. **O achado ou resultado**, quando houver +5. **O desdobramento** — o que isso destrava adiante + +Números reais e verificáveis. Se um número aparece na SD e outro no artefato entregue, a inconsistência aparece sozinha na primeira leitura cruzada. + +--- + +## 3. Quadro de entregáveis + +O coração da SD. Uma linha por entregável faturável. + +| # | Entregável | Tipo | Item TR | Semanas | Time-box | UST | Período | +|---|---|---|---|---|---|---|---| +| 1 | | | | | | | | +| 2 | | | | | | | | +| 3 | | | | | | | | +| | **Total** | | | | | **XXX** | | + +**Confira antes de seguir:** +- Cada linha: `UST = time-box × semanas` +- Nenhum entregável abaixo de 2 nem acima de 4 semanas +- Time-box de Construção correto para o item (40h ou 80h) +- Períodos de entregáveis encadeados não se sobrepõem +- Se há paralelismo, está declarado em nota +- A soma da coluna UST bate com o cabeçalho + +Quando a SD cruza itens, acrescente uma linha de subtotal por item — é assim que o controle de pool é feito. + +--- + +## 4. Detalhamento dos entregáveis + +Um bloco por entregável, na ordem do quadro. Quatro seções cada. + +``` +ENTREGÁVEL N — [Nome do artefato final] +Tipo · Item TR · X semanas · XX UST · DD/MM a DD/MM + +Resumo +Três linhas. O que o entregável fez e o que produziu. + +Atividades previstas +Bullets. Blocos de trabalho concretos, de seis a nove. + +Artefatos comprobatórios +- [Documento com nome real e, quando houver, data] +- [Documento] + +Critérios de aceite +Bullets. Condições verificáveis por terceiro. +``` + +**Sobre os critérios de aceite** — o teste é se alguém que não participou consegue dizer sim ou não olhando o artefato: + +| Serve | Não serve | +|---|---| +| "Índice reproduzível: mesma base e mesmo código produzem resultado idêntico" | "Modelo bem construído" | +| "Dashboard acessível pelos perfis indicados pela SES-MG" | "Dashboard funcionando adequadamente" | +| "Toda pergunta do formulário mapeada à dimensão que alimenta" | "Documentação completa" | + +--- + +## 5. Enquadramento no Termo de Referência + +Seção obrigatória. É a que sustenta a SD se a fiscalização contestar o item — e é a que evita a reclassificação que muda o time-box de Construção e derruba as USTs pela metade. + +| | | +|---|---| +| **Item do TR** | Número e título literal, exatamente como no TR | +| **Descrição do item** | Transcrição do escopo do item conforme o TR | +| **Aderência desta SD** | Bullets ligando o que foi entregue ao que o item prevê | + +**Regras para os bullets de aderência:** + +- Um bullet por componente entregue, de três a cinco no total +- Use o vocabulário do item, não o vocabulário interno da equipe +- Descreva o que foi construído, não o que foi discutido +- Não cite componente que a SD não tem só para reforçar o encaixe + +**Exemplo — entregáveis de índice e dashboard (I-05):** + +| | | +|---|---| +| **Item do TR** | I-05 – Serviços técnicos de inteligência e produtos analíticos de gestão em saúde | +| **Descrição do item** | Serviços técnicos de inteligência aplicada aos dados da plataforma, compreendendo a concepção e o desenvolvimento de algoritmos analíticos, modelos de classificação, índices e dashboards gerenciais destinados a apoiar a tomada de decisão das áreas demandantes da SES-MG. Inclui extração de fenótipos, clusterização e priorização de populações e territórios, apuração de indicadores e disponibilização dos resultados em painéis, relatórios e produtos analíticos de gestão. | +| **Aderência desta SD** | • Concepção do algoritmo de classificação em sete dimensões ponderadas, com penalidade, bônus de cadeia e normalização em escala de 0 a 100.
• Desenvolvimento do motor de cálculo que processa as respostas do formulário e atribui score e faixa a cada município.
• Construção do dashboard gerencial, com ranking, cadeia de envios por domínio e corte por macrorregião.
• Disponibilização do painel aos gestores da SES-MG como insumo de priorização das ondas de integração. | + +**Quando a SD cruza dois itens:** repita a seção 5 inteira para cada item, e indique quais entregáveis do quadro pertencem a cada um. + +--- + +# PARTE III — VALIDAÇÃO + +**Enquadramento** +``` +[ ] Cada entregável tem item definido (I-02 / I-03 / I-04 / I-05) +[ ] Produto novo está no I-02, não no I-04 +[ ] Dashboard, índice ou algoritmo está no I-05, não no I-04 +[ ] Automação de tarefa repetitiva está no I-04 +[ ] Treinamento pós-implantação está no I-03 +[ ] Entregáveis de itens diferentes estão em linhas separadas +[ ] Há subtotal por item quando a SD cruza itens +``` + +**Cálculo** +``` +[ ] Cada entregável entre 2 e 4 semanas +[ ] UST de cada linha = time-box × semanas +[ ] Time-box de Construção correto (40h em I-02/I-03, 80h em I-04/I-05) +[ ] Soma da coluna UST bate com o cabeçalho +[ ] Entregáveis encadeados sem sobreposição de período +[ ] Paralelismo, quando existe, está declarado +[ ] Prazo do cabeçalho reflete o calendário, não a soma das semanas +``` + +**Documento** +``` +[ ] Nome do projeto é o desta demanda, não copiado de outra SD +[ ] Número do item e título do item correspondem entre si +[ ] Título do item confere com a redação literal do TR +[ ] Números do contexto batem com os artefatos entregues +[ ] Produto tem um nome só ao longo de todo o documento +[ ] Nenhum campo confessa irregularidade +[ ] Cada entregável tem o mínimo de artefatos comprobatórios da sua faixa +[ ] Todo artefato citado existe de fato +[ ] Data final de cada entregável é coerente com a data dos seus artefatos +[ ] Todo critério de aceite é verificável por terceiro +[ ] Seção 5 preenchida com aderência específica, não genérica +``` + +--- + +# ANEXO — Referência rápida + +## Enquadramento em 4 perguntas + +``` +1. É plataforma crescendo (módulo novo, fonte nova, conector novo)? → I-02 +2. É ensinar alguém a usar algo que já foi implantado? → I-03 +3. É automatizar tarefa repetitiva / corrigir algo em produção? → I-04 +4. É criar dashboard, algoritmo, IA, busca ativa, análise? → I-05 +``` + +## USTs por entregável + +| Semanas | Desc / Design / Arq | Construção I-02 e I-03 | Construção I-04 e I-05 | +|---|---|---|---| +| 2 | 80 | 80 | 160 | +| 3 | 120 | 120 | 240 | +| 4 | 160 | 160 | 320 | + +## Artefatos comprobatórios por entregável + +| USTs | Artefatos mínimos | +|---|---| +| 80 | 1 | +| 120 a 160 | 2 | +| 240+ | 3 | + +## Conjunto típico de entregáveis (3 + 2 + 2 + 3 semanas) + +| Item | Descoberta | Design | Arquitetura | Construção | **Total** | +|---|---|---|---|---|---| +| I-02 | 120 | 80 | 80 | 120 | **400** | +| I-03 | 120 | 80 | 80 | 120 | **400** | +| I-04 | 120 | 80 | 80 | 240 | **520** | +| I-05 | 120 | 80 | 80 | 240 | **520** | + +## Estrutura da SD + +``` +Cabeçalho (identificação) +1. Objetivo +2. Contexto e escopo da demanda +3. Quadro de entregáveis (tipo × item × semanas × time-box × UST × período) +4. Detalhamento dos entregáveis + 4.N Resumo (3 linhas) + 4.N Atividades previstas (bullets) + 4.N Artefatos comprobatórios (nomeados) + 4.N Critérios de aceite (bullets verificáveis) +5. Enquadramento no Termo de Referência + Item do TR / Descrição do item / Aderência desta SD +``` + +--- + +*Documento de apoio à formalização de demandas — RMDS / SES-MG.* diff --git a/P2-SD8_MVP_VacinaEmDia.md b/P2-SD8_MVP_VacinaEmDia.md new file mode 100644 index 0000000..eb7119e --- /dev/null +++ b/P2-SD8_MVP_VacinaEmDia.md @@ -0,0 +1,254 @@ +**SOLICITAÇÃO DE DEMANDA (SD)** + +**MVP – Vacina em Dia** + +*Caderneta Vacinal Digital no MG App com Dados do PNI e Alertas de Pendências* + +| Cliente / Contratante | Secretaria de Estado de Saúde de Minas Gerais (SES-MG) | +| :---- | :---- | +| **Contratada** | Isis Saúde | +| **Projeto / Solução** | P2 \- Vacinação | +| **Nº da SD** | SD8 | +| **Versão** | V1 | +| **Data de Abertura** | 01/05/2026 | +| **Item do Termo de Referência** | I-02 e I-05 | +| **Quantidade de UST Estimadas** | 1120 (mil cento e vinte) UST | +| **Prazo de Execução Estimado** | 5 (cinco) semanas | +| **Sprints Contempladas** | Descoberta, Design, Arquitetura e Construção | + +# **1\. Objetivo** + +Entregar ao cidadão mineiro a caderneta vacinal digital: histórico de imunização unificado a partir da RNDS, com identificação por CNS/CPF e visão longitudinal por pessoa. + +# **2\. Justificativa** + +O histórico vacinal do cidadão está fragmentado entre municípios, sistemas e cadernetas de papel — não existe visão única nem para o cidadão nem para o gestor. Esta SD é a primeira prova pública da RMDS: demonstra que a plataforma unifica identidade e linha do tempo do cidadão, e cria a porta de entrada de relacionamento com a população. + +# **3\. Contexto** + +O projeto Vacina em Dia consiste no desenvolvimento e implementação de um ecossistema digital inteligente voltado à gestão da saúde vacinal do cidadão. A demanda origina-se da necessidade estrita da Secretaria de Estado de Saúde de Minas Gerais (SES-MG) de prover transparência, acessibilidade e resolutividade clínica à população mineira diretamente por meio do MG App (o super-app de serviços públicos do Estado). + +O motor opera de forma determinística, cruzando ativamente o histórico vacinal em tempo real do indivíduo (extraído via integração escalável com a base nacional do PNI/RNDS) com as diretrizes normativas vigentes do Programa Nacional de Imunizações (PNI 2026), do Manual de Centros de Referência para Imunobiológicos Especiais (CRIE) e das resoluções técnicas estaduais da SES-MG + +O desenvolvimento e a homologação do sistema submeteram-se a uma restrição temporal: + +* Janela de Execução: O projeto foi planejado e codificado em um ciclo ágil e ininterrupto de 02/06/2026 a 03/07/2026 +* Marco de Publicação Impositiva: A disponibilização final em produção ocorreu em 02 de julho de 2026, às 11h00, cumprindo a obrigatoriedade institucional de entrar em operação pelo menos 24 horas antes do início do período de vedação eleitoral (estabelecido em 04 de julho de 2026), inviabilizando qualquer direito a retrabalho ou janelas de hotfix pós-prazo. + +# **4\. Escopo da Demanda** + +**Públicos e perfis**: calendário por faixa etária \+ profissionais de saúde \+ gestantes; perfil ativado por triagem autodeclarada no formulário. + +**Motor de regras**: determinístico, em memória, 60+ regras vacinais em arquivos versionados, auditáveis linha a linha. Lógicas críticas: Febre Amarela (imunização) e dT/dTpa (contagem de doses). + +**Sinalização da caderneta**: 5 status: Em Dia · Próxima dose · Atrasada · Não Digitalizada (sem registro digital para doses esperadas pré-2020) · Aguarda Semana (gestante que ainda não atingiu a IG mínima). + +**Filtro de segurança clínica**: ocultação silenciosa e sistemática de vacinas vivas atenuadas (Tríplice Viral, Varicela, Dengue) quando o perfil ativo é gestante. + +**Contingência de campanhas**: notas explicativas para Influenza e COVID-19, cujo histórico pode residir em índices de campanha (RCA) não consumidos pela API de rotina no MVP. + +**Regularização**: canal direto para o WhatsApp institucional da SES-MG. + +**Arquitetura:** hospedado em nuvem própria Isis integrado ao MG App. + +# + +# + +# + +# + +# **5\. Escopo por Sprint** + +## 5.1 · DESCOBERTA (D) + +### Discovery Negocial e Institucional · I-02 · 120 USTs · 3 Semanas + +* Definição de público-alvo e recorte do MVP +* Validação institucional do escopo com Sub-secretaria de Vigilância SES-MG +* Consolidação de decisões sobre perfis, funcionalidades e exclusões +* Alinhamento jurídico/LGPD + +### Discovery Técnico Integrado · I-02 · 120 USTs · 3 Semanas + +* Análise da fonte PNI via bucket Elastic Search +* Mapeamento empírico do catálogo de vacinas +* Documentação do contrato de dados e schema do bucket +* Definição da arquitetura de embedamento no MGapp em alinhamentos técnicos com Prodemge (SEPLAG) + +### Discovery Clínico do Motor de Regras · I-05 · 80 USTs · 2 Semanas + +* Levantamento das regras vacinais por calendário e perfil (universo teórico) +* Recorte para MVP simplificado +* Definição da hierarquia clínica de resolução de conflitos +* Definição dos 6 status +* Definição de regras de corte temporal 01/01/2020 para "não digitalizada" +* Estruturação do vocabulário fechado de contraindicações + +## 5.2 · DESIGN (DE) + +### Design do MVP \- Vacina em Dia · I-02 · 80 USTs · 2 Semanas + +* Desenho do fluxo do formulário em 2 camadas (multi-seleção \+ condicional) +* Design das 12 telas do MVP +* Microcopy revisada e aprovada pelo PO +* Tratamento de casos de borda (gestante sem semana, condição especial redirecionada) +* Especificação visual dos status (paleta, iconografia, tipografia) +* Regras de exibição por perfil (BLOQUEADA oculta ao cidadão; AGUARDA IG apenas gestante) +* Microcopy do canal de regularização (WhatsApp SES) +* Aplicação de identidade institucional (brasão MG, SES-MG, Zé Gotinha) + +### Design da Modelagem Clínica do Motor · I-05 · 80 USTs · 2 Semanas + +* Análise de alternativas de motor e justificativa da escolha +* Desenho de casos de uso clinicamente relevantes (personas) +* Desenho do formulário de triagem — o que perguntar, inferir e não perguntar +* Estratégias de fallback (PNI vazio, corrompido, parcial) +* Decisões clínicas de design (hierarquia de conflitos, precedência de contraindicação) +* Subfunções clínicas especiais (Febre Amarela pré-5a · dT esquema acumulado) +* Trilha de validação clínica com SVE + +## 5.3 · ARQUITETURA (A) + +### Arquitetura da Aplicação · I-02 · 160 USTs · 4 Semanas + +* Definição de stack e componentes +* Especificação dos fluxos de comunicação (consulta PNI, autenticação, calendário) +* Especificação da integração com MGapp +* Definição das tabelas de domínio e de catálogo +* Solução para não-persistência de dado vacinal +* Especificação de APIs de autenticação (Swagger) e zonas de segurança + +### Arquitetura Técnica do Motor · I-05 · 80 USTs · 2 Semanas + +* Especificação do motor +* Definição das etapas de processamento +* Definição da hierarquia de conflitos implementável +* Contrato do endpoint +* Estratégia de carregamento das regras e de testabilidade +* Registro de gatilhos de refatoração + +## 5.4 · CONSTRUÇÃO + +### Backend \+ Integração PNI · I-02 · 160 USTs · 4 Semanas + +* Implementação das APIs REST conforme contratos +* Implementação da chamada ao middleware Azure SES-MG (login → token → search) +* Implementação dos 3 mappers de sigla PNI → vacina canônica +* Tratamento de códigos PNI corrompidos +* Consolidação do Swagger +* Testes unitários e de integração com medição de cobertura + +### Frontend \+ Publicação no MGApp · I-02 · 80 USTs · 2 Semanas + +* Implementação das telas e do formulário +* Aplicação da identidade visual (Zé Gotinha em 3 pontos) +* Otimização mobile-first +* Telas de fallback (aviso desktop · erro "acesse pelo MG App") +* Consumo das APIs e tratamento de erros na UI +* Publicação da URL (DNS ATI SES) +* Recebimento e validação do token JWT +* Cadastro da integração no back-office do MG App (Prodemge) + +### Construção e Validação Final do Motor · I-05 · 160 USTs · 2 Semanas + +* Carregamento das regras ativas +* Implementação das etapas de processamento +* Implementação da hierarquia de resolução de conflitos +* Execução dos testes das personas e de casos adicionais +* Disponibilização do endpoint +* Sessão de validação clínica final com SVE +* Incorporação de ajustes clínicos e testes de validação + + +# + +# + +# + +# + +# + +# + +# + +# + +# + +# + +# + +# **6\. Enquadramento no Termo de Referência** + +| Item | Título | Descrição | USTs | +| :---- | :---- | :---- | :---- | +| I-02 | Implementação e Desenvolvimento da Solução: módulos, integrações e camada blockchain | Executar atividades de desenvolvimento de software para soluções com blockchain, incluindo contratos inteligentes e atividades relacionadas com a integração com sistemas legados. O escopo abrange, além da camada blockchain, o desenvolvimento de interfaces e APIs, a integração com sistemas de informação existentes da SES-MG, a conformidade com padrões de interoperabilidade, a segurança da informação com controle de acesso por identidade digital, a rastreabilidade e a conformidade com a LGPD e a PNSI. | Desenvolvimento de módulo da solução RMDS — interfaces, integração com sistema legado, autenticação por identidade digital e implantação segura. Desenvolvimento de interfaces: APIs REST documentadas em Swagger e front-end do Vacina em Dia. Conformidade LGPD Implantação: publicação em produção | +| | | | | +| I-05 | Serviços técnicos de inteligência e produtos analíticos de gestão em saúde | Serviços técnicos de inteligência aplicada aos dados da plataforma, compreendendo a concepção e o desenvolvimento de algoritmos analíticos, modelos de classificação, índices e dashboards gerenciais destinados a apoiar a tomada de decisão das áreas demandantes da SES-MG. Inclui extração de fenótipos, clusterização e priorização de populações e territórios, apuração de indicadores e disponibilização dos resultados em painéis, relatórios e produtos analíticos de gestão. | Modelagem do algoritmo: regras vacinais ativas com hierarquia de resolução de conflitos entre perfis.Incluindo regras de segurança clínica "De-para" semântico: mapeamento de 103 siglas do índice PNI/MG para vocabulário canônico de vacina. | +| | | | | + +# **7\. Estimativa de Esforço e Prazo** + +Para a execução integral desta demanda, contemplando as fases de Descoberta, Design, Arquitetura e Construção, foi estimado o esforço de 1120 (mil cento e vinte) UST, a serem executadas no prazo de 5 (cinco) semanas. + +| Quantidade de UST | Prazo de Execução | Início Previsto | Fim Previsto | +| :---: | :---: | :---: | :---: | +| **1.120 UST** | **4,42 semanas** | **02/06/2026** | **04/07/2026** | + +| Fase | UST | Item TR | Timebox (Semanas) | Valor (R$) | +| ----- | :---: | :---: | :---: | :---: | +| **Descoberta** | 240 | I-02 | 6 | 127.999,20 | +| | 80 | I-05 | 2 | 44.444,00 | +| **Design** | 80 | I-02 | 2 | 42.666,40 | +| | 80 | I-05 | 2 | 44.444,00 | +| **Arquitetura** | 160 | I-02 | 4 | 85.332,80 | +| | 80 | I-05 | 2 | 44.444,00 | +| **Construção** | 240 | I-02 | 4 | 127.999,20 | +| | 160 | I-05 | 2 | 88.888,00 | +| **Total** | **1.120** | **I-02, I-05** | **24** | **R$ 606.217,60** | + +# + +# + +# + +# + +# + +# + +# + +# + +# + +# **8\. Aprovação** + +Declaro estar de acordo com a Solicitação de Demanda apresentada pela contratada Isis Saúde, avaliados pela equipe técnica da Secretaria de Estado de Saúde de Minas Gerais e estão em conformidade com os requisitos estabelecidos no Termo de Referência do Pregão Eletrônico nº 29/2025, observados os termos, escopo, atividades e esforço descritos nesta Solicitação de Demanda. + +| Product Owner | César Augusto Castro Alves Martins | +| :---- | :---- | +| Fiscal do Contrato | Guilherme Amaral Bernardino | +| Gestor do Contrato | Rafael Matos Paiva | + +## + +## 8.1. Formalização do Aceite + +Assinatura digital do aceite por meio do sistema Gov.br: + +*Assinatura — Product Owner* + +*Assinatura — Fiscal do Contrato* + +*Assinatura — Gestor do Contrato* + diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/SD/SD-9 - Vacina em Dia.pdf b/SD/SD-9 - Vacina em Dia.pdf new file mode 100644 index 0000000..b975827 Binary files /dev/null and b/SD/SD-9 - Vacina em Dia.pdf differ diff --git a/SD/copia_base-conhecimento.json b/SD/copia_base-conhecimento.json new file mode 100644 index 0000000..2677df5 --- /dev/null +++ b/SD/copia_base-conhecimento.json @@ -0,0 +1,43 @@ +{ + "$comment": "Base de conhecimento do redator de SDs — RMDS / SES-MG (PE 29/2025). Terceiro arquivo da arquitetura: regras-sd.json = regras; schema-sd.json = formato; ESTE arquivo = histórico. Anexe-o ao projeto/Cowork junto com os outros dois. PREENCHIMENTO: apague os registros de exemplo (id terminando em '-EXEMPLO') e cadastre apenas fatos reais — o redator é proibido de inventar precedentes, e esta base só serve se for verdadeira. Uma linha nova em 'sds' a cada SD enviada; uma em 'capacidades' a cada capacidade entregue.", + "versao": "0.1.0", + "atualizado_em": "2026-08-07", + + "convencoes": { + "status_sd": ["rascunho", "enviada", "aprovada", "em_execucao", "concluida", "faturada", "cancelada"], + "status_que_consomem_pool": ["enviada", "aprovada", "em_execucao", "concluida", "faturada"], + "status_capacidade": ["em_construcao", "entregue"], + "saldo_de_pool": "saldo(item) = pool_ust_total do item em regras-sd.json − soma de ust_por_item das SDs cujo status consome pool", + "tags": "minúsculas, sem acento, singular quando possível; ex.: dashboard, integracao, niten, regulacao, vacinacao, treinamento, automacao", + "ids": "SD-NN segue a numeração oficial do contrato; CAP-NN é sequencial interno desta base" + }, + + "sds": [ + { + "id": "SD-00-EXEMPLO", + "$comment": "REGISTRO DE EXEMPLO — apague e substitua pelas SDs reais do contrato.", + "titulo": "Painel de fila cirúrgica da macrorregião Centro", + "projeto": "Fila Cirúrgica", + "itens_tr": ["I-05"], + "ust_total": 240, + "ust_por_item": { "I-05": 240 }, + "status": "faturada", + "periodo": { "inicio": "2026-01-05", "fim": "2026-01-23" }, + "resumo": "Uma frase dizendo o que a SD entregou e para quem.", + "tags": ["dashboard", "regulacao", "fila-cirurgica"] + } + ], + + "capacidades": [ + { + "id": "CAP-00-EXEMPLO", + "$comment": "REGISTRO DE EXEMPLO — apague e substitua pelas capacidades reais já entregues na plataforma.", + "titulo": "Integração com o barramento de dados do sistema XYZ", + "descricao": "O que a plataforma já sabe fazer, escrito para responder à pergunta 'isso precisa ser construído de novo?'", + "origem_sd": "SD-00-EXEMPLO", + "item_tr": "I-02", + "status": "entregue", + "tags": ["integracao", "xyz"] + } + ] +} diff --git a/app/.env.example b/app/.env.example new file mode 100644 index 0000000..88fdb29 --- /dev/null +++ b/app/.env.example @@ -0,0 +1,29 @@ +# Copie este arquivo para ".env" (na mesma pasta) e preencha a chave. +# O arquivo .env NUNCA deve ser versionado nem compartilhado. + +# Chave da API da Anthropic (crie em https://console.anthropic.com -> API Keys) +ANTHROPIC_API_KEY= + +# Provedor de LLM (por enquanto apenas "anthropic"; adaptador OpenAI previsto para a fase 1.1) +LLM_PROVIDER=anthropic + +# Modelo padrão +LLM_MODEL=claude-opus-5 + +# Modelos oferecidos nos seletores da interface (Nova SD e Base de Conhecimento), +# separados por vírgula. O LLM_MODEL acima sempre entra na lista, como padrão. +# Obs.: o custo estimado no rodapé usa a tabela de preços do Opus. +LLM_MODELOS=claude-sonnet-5,claude-haiku-4-5-20251001 + +# Esforço de raciocínio do modelo: low | medium | high | xhigh | max +# (vazio = padrão da API, que é high). "medium" acelera bem a entrevista +# com perda mínima de qualidade; suba para high se notar respostas rasas. +LLM_EFFORT=medium + +# Porta do servidor local +PORT=3344 + +# Login do app (usuário único). Enquanto os dois campos estiverem vazios, +# o app roda SEM login e mostra um aviso. +APP_USUARIO= +APP_SENHA= diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..5235962 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +.env +data/ diff --git a/app/README.md b/app/README.md new file mode 100644 index 0000000..aa91bc5 --- /dev/null +++ b/app/README.md @@ -0,0 +1,88 @@ +# Redator de SD — RMDS / SES-MG × Isis Saúde + +App local que conduz a entrevista de redação de Solicitações de Demanda (SD) com um LLM, +valida em código contra as regras do contrato (PE 29/2025) e mantém a base de conhecimento. + +## Primeira execução + +1. **Chave de API** — crie em https://console.anthropic.com → *API Keys* e coloque um crédito + inicial pequeno (US$ 5–10 rendem dezenas de SDs). +2. **Configuração** — copie `.env.example` para `.env` (nesta pasta) e preencha + `ANTHROPIC_API_KEY`. Defina também `APP_USUARIO` e `APP_SENHA` para ativar a + tela de login (enquanto vazios, o app roda aberto e mostra um aviso). + O `.env` nunca deve ser versionado nem compartilhado. +3. **Dependências** (só na primeira vez): + ``` + cd D:\IasisSaude\app + npm install + ``` +4. **Rodar**: + ``` + npm start + ``` + e abra http://localhost:3344 no navegador. + +## Como funciona + +- **Nova SD**: cole a demanda bruta → o redator entrevista você (uma pergunta por turno, + opções viram botões) enquanto o painel do documento se atualiza ao vivo. +- **Validação**: os itens `codigo` do checklist são recalculados em JavaScript a cada + atualização — e o resultado é realimentado ao modelo, que se corrige sozinho. +- **Trava de integridade**: exportação e envio ficam bloqueados até todos os artefatos + serem confirmados (checkbox + data) e nenhuma falha de código restar. +- **Envio**: atribui o número definitivo (SD-NN) e registra a SD em + `..\base-conhecimento.json` — saldo de pools e precedentes ficam atualizados + para as próximas entrevistas. +- **Documento / impressão**: o botão "Documento / imprimir (PDF)" abre a SD formatada + como documento oficial em uma aba pronta para Ctrl+P → salvar como PDF. Com pendências + bloqueantes abertas, o documento sai com marca de RASCUNHO. +- **Validar JSON externo** (menu no topo): cole o JSON de uma SD que não nasceu no app — + o validador recalcula tudo, roda o checklist e libera documento/download, sem salvar nada. +- **Alimentar base** (menu no topo): envie o documento de uma SD real (`.pdf`, `.md` ou `.txt`; + `.docx` deve ser exportado como PDF). Uma chamada de LLM extrai o registro estruturado + (itens, UST por item, período, status) e sugere capacidades reutilizáveis; a conferência + determinística em código checa somas, itens, status e impacto nos pools. **Nada é gravado + sem revisão**: você edita o registro, marca/desmarca capacidades e só então grava em + `..\base-conhecimento.json`. Erros de conferência bloqueiam a gravação; regravar o mesmo + SD-NN substitui o registro anterior (capacidades, porém, são sempre acrescentadas — ao + regravar uma SD, desmarque as que já entraram na primeira vez). +- **Login**: usuário único definido no `.env`. As sessões vivem na memória do servidor — + reiniciar o servidor exige novo login. + +## Arquivos de configuração (raiz do projeto, `D:\IasisSaude`) + +| Arquivo | Papel | +|---|---| +| `prompt-redator-sd.md` | Comportamento do redator (system prompt) | +| `regras-sd.json` | Regras duras do contrato — fonte única | +| `schema-sd.json` | Formato da SD | +| `base-conhecimento.json` | Histórico: SDs enviadas e capacidades da plataforma | + +Editar qualquer um deles muda o app na próxima reinicialização — não é preciso mexer em código. + +## Custos + +O rodapé do chat mostra tokens e custo estimado em tempo real. O system prompt +(prompt + regras + base) é cacheado pela API — turnos seguintes da mesma conversa +pagam ~10% do preço no prefixo repetido. + +## Dados + +- `data/sds.json` — SDs e conversas (criado automaticamente; fora do versionamento). +- Provedor/modelo/esforço configuráveis no `.env` (`LLM_PROVIDER`, `LLM_MODEL`, + `LLM_EFFORT` — low/medium/high/xhigh/max; medium recomendado para a entrevista). + Adaptador OpenAI previsto para a fase 1.1 (`src/llm/provider.js`). +- **Seletor de modelo**: `LLM_MODELOS` (lista separada por vírgula) define os modelos + oferecidos nos seletores das telas Nova SD e Base de Conhecimento; o `LLM_MODEL` + sempre entra como padrão. O modelo escolhido fica fixo para a SD inteira — trocar + no meio da conversa descartaria o cache de prompt. O custo do rodapé usa a tabela + de preços do Opus (a observação ao lado do valor lembra isso). + +## Atualização dos arquivos de configuração + +O prompt do redator é remontado automaticamente quando `base-conhecimento.json`, +`prompt-redator-sd.md`, `regras-sd.json` ou `schema-sd.json` mudam no disco — +vale a partir da PRÓXIMA conversa, sem reiniciar o servidor. Exceção: mudanças em +`regras-sd.json`/`schema-sd.json` também alimentam o validador em código e a +ferramenta do modelo, que carregam na subida — para essas duas, reinicie. +Mudanças no `.env` sempre exigem reinício. diff --git a/app/Subir_aplicacao.txt b/app/Subir_aplicacao.txt new file mode 100644 index 0000000..48c6dc1 --- /dev/null +++ b/app/Subir_aplicacao.txt @@ -0,0 +1,3 @@ +Subir Aplicação + +cd D:\IasisSaude\app && npm start \ No newline at end of file diff --git a/app/package-lock.json b/app/package-lock.json new file mode 100644 index 0000000..c6fa60c --- /dev/null +++ b/app/package-lock.json @@ -0,0 +1,890 @@ +{ + "name": "redator-sd", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "redator-sd", + "version": "0.1.0", + "dependencies": { + "@anthropic-ai/sdk": "^0.74.0", + "dotenv": "^16.4.5", + "express": "^4.21.0" + } + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.74.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.74.0.tgz", + "integrity": "sha512-srbJV7JKsc5cQ6eVuFzjZO7UR3xEPJqPamHFIe29bs38Ij2IripoAhC0S5NslNbaFUYqBKypmmpzMTpqfHEUDw==", + "license": "MIT", + "dependencies": { + "json-schema-to-ts": "^3.1.1" + }, + "bin": { + "anthropic-ai-sdk": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", + "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/json-schema-to-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", + "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "ts-algebra": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-algebra": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", + "license": "MIT" + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + } + } +} diff --git a/app/package.json b/app/package.json new file mode 100644 index 0000000..4cb4cfa --- /dev/null +++ b/app/package.json @@ -0,0 +1,15 @@ +{ + "name": "redator-sd", + "version": "0.1.0", + "description": "Redator de Solicitações de Demanda (SD) — RMDS / SES-MG × Isis Saúde", + "type": "module", + "private": true, + "scripts": { + "start": "node src/server.js" + }, + "dependencies": { + "@anthropic-ai/sdk": "^0.74.0", + "dotenv": "^16.4.5", + "express": "^4.21.0" + } +} diff --git a/app/public/app.js b/app/public/app.js new file mode 100644 index 0000000..bd60af9 --- /dev/null +++ b/app/public/app.js @@ -0,0 +1,980 @@ +"use strict"; + +const $ = (id) => document.getElementById(id); +const esc = (s) => + String(s ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c])); +const fmtD = (iso) => { + const m = String(iso || "").match(/^(\d{4})-(\d{2})-(\d{2})/); + return m ? `${m[3]}/${m[2]}/${m[1]}` : "—"; +}; +const dayNum = (iso) => { + const m = String(iso || "").match(/^(\d{4})-(\d{2})-(\d{2})/); + return m ? Date.UTC(+m[1], +m[2] - 1, +m[3]) / 86400000 : NaN; +}; + +// Preços do claude-opus-5 (USD por 1M tokens) para o custo estimado no rodapé. +const PRECO = { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }; + +let statusApp = null; +let sdAtual = null; // { id, numero, status, chat, rascunho, validacao, bloqueios, uso } +let ocupado = false; +let modoExterno = false; // tela "Validar JSON externo" +let externo = null; // { sd, exportado, validacao, bloqueios } +let ultimosBloqueios = []; + +// ---------- Markdown mínimo (negrito, código, títulos, listas, OPCAO:) ---------- +function renderMd(texto) { + const linhas = String(texto || "").split("\n"); + const out = []; + let lista = null; // "ul" | "ol" + const fechaLista = () => { if (lista) { out.push(``); lista = null; } }; + const inline = (s) => + esc(s) + .replace(/\*\*([^*]+)\*\*/g, "$1") + .replace(/`([^`]+)`/g, "$1"); + for (const raw of linhas) { + const l = raw.trimEnd(); + if (/^OPCAO:/i.test(l.trim())) continue; // vira botão, não texto + const h = l.match(/^(#{1,4})\s+(.*)/); + const ul = l.match(/^\s*[-•]\s+(.*)/); + const ol = l.match(/^\s*\d+[.)]\s+(.*)/); + if (h) { fechaLista(); out.push(`

${inline(h[2])}

`); } + else if (ul) { if (lista !== "ul") { fechaLista(); out.push("