@useinvio/nfse-sdk 3.0.0 → 3.1.0
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/README.md +115 -56
- package/dist/agent.d.ts +86 -0
- package/dist/agent.js +235 -0
- package/dist/agent.js.map +1 -0
- package/dist/certificate.d.ts +96 -0
- package/dist/certificate.js +218 -0
- package/dist/certificate.js.map +1 -0
- package/dist/certificateValidation.d.ts +11 -6
- package/dist/certificateValidation.js +23 -12
- package/dist/certificateValidation.js.map +1 -1
- package/dist/client.d.ts +4 -2
- package/dist/client.js +5 -4
- package/dist/client.js.map +1 -1
- package/dist/dpsJsonSchema.d.ts +17 -0
- package/dist/dpsJsonSchema.js +184 -0
- package/dist/dpsJsonSchema.js.map +1 -0
- package/dist/emissaoNota.d.ts +4 -4
- package/dist/emissaoNota.js +4 -3
- package/dist/emissaoNota.js.map +1 -1
- package/dist/fiscalDomains.d.ts +22 -0
- package/dist/fiscalDomains.js +99 -0
- package/dist/fiscalDomains.js.map +1 -0
- package/dist/fiscalValidation.js +21 -53
- package/dist/fiscalValidation.js.map +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/jsonSchemaCheck.d.ts +12 -0
- package/dist/jsonSchemaCheck.js +70 -0
- package/dist/jsonSchemaCheck.js.map +1 -0
- package/dist/loadPfx.d.ts +2 -0
- package/dist/loadPfx.js +2 -1
- package/dist/loadPfx.js.map +1 -1
- package/dist/sefinClient.d.ts +10 -4
- package/dist/sefinClient.js +22 -4
- package/dist/sefinClient.js.map +1 -1
- package/dist/signXml.d.ts +3 -3
- package/dist/signXml.js +8 -6
- package/dist/signXml.js.map +1 -1
- package/dist/xmlInspection.js +10 -9
- package/dist/xmlInspection.js.map +1 -1
- package/dist/xsdValidation.d.ts +17 -5
- package/dist/xsdValidation.js +75 -67
- package/dist/xsdValidation.js.map +1 -1
- package/examples/README.md +1 -1
- package/examples/client-resource.ts +3 -5
- package/examples/emit-homologation.ts +3 -4
- package/examples/handle-errors.ts +3 -3
- package/llms.txt +15 -5
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -40,11 +40,11 @@ O SDK cuida da camada protocolar: certificado A1, montagem de DPS, validações
|
|
|
40
40
|
- [Enviar evento](#enviar-evento-ex-cancelamento)
|
|
41
41
|
- [Série e número da DPS](#série-e-número-da-dps)
|
|
42
42
|
- [Ambientes](#ambientes)
|
|
43
|
+
- [Agentes de IA e MCP](#agentes-de-ia-e-mcp)
|
|
43
44
|
- [Referência da API](#referência-da-api)
|
|
44
45
|
- [Desenvolvimento](#desenvolvimento)
|
|
45
46
|
- [Publicação no npm](#publicação-no-npm)
|
|
46
|
-
- [
|
|
47
|
-
- [Assistente GPT](#assistente-gpt)
|
|
47
|
+
- [Exemplos](#exemplos)
|
|
48
48
|
|
|
49
49
|
---
|
|
50
50
|
|
|
@@ -54,7 +54,7 @@ O SDK cuida da camada protocolar: certificado A1, montagem de DPS, validações
|
|
|
54
54
|
npm install @useinvio/nfse-sdk
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
> Node.js ≥ 20 é obrigatório.
|
|
57
|
+
> Node.js ≥ 20 é obrigatório. Não precisa de `xmllint` nem de binários nativos: a validação XSD roda em WebAssembly.
|
|
58
58
|
|
|
59
59
|
---
|
|
60
60
|
|
|
@@ -63,9 +63,10 @@ npm install @useinvio/nfse-sdk
|
|
|
63
63
|
O caminho mais comum: emitir uma nota a partir de JSON declarativo.
|
|
64
64
|
|
|
65
65
|
```ts
|
|
66
|
-
import { emitirNfse,
|
|
66
|
+
import { emitirNfse, EmitirNotaError, NfseCertificate } from '@useinvio/nfse-sdk';
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
// NFSE_CERT_PATH=./certificado.pfx e NFSE_CERT_PASSWORD=... (veja Certificado A1)
|
|
69
|
+
const certificado = NfseCertificate.fromEnv();
|
|
69
70
|
|
|
70
71
|
const nota = {
|
|
71
72
|
ambiente: 'restrita' as const,
|
|
@@ -103,7 +104,7 @@ const nota = {
|
|
|
103
104
|
};
|
|
104
105
|
|
|
105
106
|
try {
|
|
106
|
-
const resultado = await emitirNfse(nota,
|
|
107
|
+
const resultado = await emitirNfse(nota, certificado);
|
|
107
108
|
console.log(resultado.chaveAcesso); // chave de 50 dígitos
|
|
108
109
|
console.log(resultado.nfseXml); // XML autorizado pela SEFIN
|
|
109
110
|
} catch (error) {
|
|
@@ -133,8 +134,8 @@ try {
|
|
|
133
134
|
| Situação | O que usar |
|
|
134
135
|
|---|---|
|
|
135
136
|
| Quer uma API de cliente com `client.invoices.create(...)` | `new NfseClient(...)` |
|
|
136
|
-
| Outro sistema já gera o XML da DPS | `emitirNfse(xmlString,
|
|
137
|
-
| Você monta os dados e quer que a SDK construa o XML | `emitirNfse(notaJson,
|
|
137
|
+
| Outro sistema já gera o XML da DPS | `emitirNfse(xmlString, certificado)` |
|
|
138
|
+
| Você monta os dados e quer que a SDK construa o XML | `emitirNfse(notaJson, certificado)` |
|
|
138
139
|
| Precisa inspecionar ou salvar o XML antes de enviar | `buildDpsFromJson(nota)` |
|
|
139
140
|
| Fluxo customizado (assinar, compactar ou enviar separadamente) | funções de baixo nível |
|
|
140
141
|
|
|
@@ -150,14 +151,11 @@ O cliente usa os mesmos campos do JSON declarativo (`prestador`, `servico` e
|
|
|
150
151
|
cada chamada.
|
|
151
152
|
|
|
152
153
|
```ts
|
|
153
|
-
import { NfseClient } from '@useinvio/nfse-sdk';
|
|
154
|
+
import { NfseCertificate, NfseClient } from '@useinvio/nfse-sdk';
|
|
154
155
|
|
|
155
156
|
const client = new NfseClient({
|
|
156
157
|
environment: 'sandbox',
|
|
157
|
-
certificate:
|
|
158
|
-
content: process.env.NFSE_CERTIFICATE!, // PFX em base64
|
|
159
|
-
password: process.env.NFSE_CERTIFICATE_PASSWORD!,
|
|
160
|
-
},
|
|
158
|
+
certificate: NfseCertificate.fromEnv(),
|
|
161
159
|
defaults: {
|
|
162
160
|
prestador: {
|
|
163
161
|
cnpj: '12345678000195',
|
|
@@ -206,9 +204,8 @@ console.log(invoice.nfseXml);
|
|
|
206
204
|
```
|
|
207
205
|
|
|
208
206
|
`environment: 'sandbox'` é alias de `restrita`, e `production` é alias de
|
|
209
|
-
`producao`.
|
|
210
|
-
`
|
|
211
|
-
ambiente.
|
|
207
|
+
`producao`. `certificate` aceita um `NfseCertificate` ou, por compatibilidade,
|
|
208
|
+
`{ content, password }` com o PFX em `Buffer` ou base64.
|
|
212
209
|
|
|
213
210
|
Para inspecionar a DPS gerada sem assinar nem enviar:
|
|
214
211
|
|
|
@@ -235,7 +232,7 @@ const dps = client.invoices.buildDpsJson({
|
|
|
235
232
|
|
|
236
233
|
Divergencias entre `vServ` e a conversao cambial geram avisos, preservando o valor explicito. O mesmo vale para o valor estrangeiro do bloco de comercio exterior. Todos os valores monetarios sao arredondados para duas casas, inclusive `comExt.vServMoeda`; valores de servico que arredondam para zero sao rejeitados. Leia `warnings` antes da emissao quando quiser apresentar esses avisos ao usuario.
|
|
237
234
|
|
|
238
|
-
`prepararNota` verifica a vigencia do certificado antes de assinar; `transmitirNotaPreparada` verifica novamente antes do envio. `assertCertificateValidity(
|
|
235
|
+
`prepararNota` verifica a vigencia do certificado antes de assinar; `transmitirNotaPreparada` verifica novamente antes do envio. `assertCertificateValidity(certificado, now?)` expoe essa verificacao. A checagem de titularidade continua explicita via `assertCertificateForProvider(certificado, documento, now?)`; o SDK nao resolve representacao fiscal.
|
|
239
236
|
|
|
240
237
|
O ambiente para JSON segue `options.ambiente` → `request.ambiente` → `restrita`, tanto no XML quanto no destino. XML externo usa `tpAmb` e rejeita uma opcao divergente. Notas preparadas tambem precisam ter o mesmo ambiente no XML assinado e no envio.
|
|
241
238
|
|
|
@@ -249,23 +246,66 @@ Veja [as correcoes e mudancas de comportamento](docs/CORRECOES_EMISSAO.md) e os
|
|
|
249
246
|
|
|
250
247
|
## Certificado A1
|
|
251
248
|
|
|
252
|
-
|
|
249
|
+
Use `NfseCertificate`. Ele lê o `.pfx`/`.p12` uma vez, mantém a chave privada fora do objeto e descarta a senha e os bytes do arquivo. `console.log(certificado)` e `JSON.stringify(certificado)` mostram só CNPJ/CPF, validade e fingerprint.
|
|
250
|
+
|
|
251
|
+
### Variáveis de ambiente (recomendado)
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
NFSE_CERT_PATH=./certificado.pfx # ou NFSE_CERT_BASE64=<PFX em base64>
|
|
255
|
+
NFSE_CERT_PASSWORD=sua-senha # ou NFSE_CERT_PASSWORD_FILE=/run/secrets/nfse_cert_password
|
|
256
|
+
```
|
|
253
257
|
|
|
254
258
|
```ts
|
|
255
|
-
import {
|
|
259
|
+
import { NfseCertificate } from '@useinvio/nfse-sdk';
|
|
256
260
|
|
|
257
|
-
const
|
|
261
|
+
const certificado = NfseCertificate.fromEnv();
|
|
262
|
+
console.log(certificado.cnpj, certificado.notAfter, certificado.daysUntilExpiry());
|
|
258
263
|
```
|
|
259
264
|
|
|
260
|
-
|
|
265
|
+
`NFSE_CERT_PASSWORD_FILE` lê a senha de um arquivo, o formato de secrets do Docker e do Kubernetes. Nunca versione o `.pfx` nem a senha.
|
|
266
|
+
|
|
267
|
+
### Outras origens
|
|
261
268
|
|
|
262
269
|
```ts
|
|
263
|
-
|
|
270
|
+
NfseCertificate.fromFile('./certificado.pfx', senha);
|
|
271
|
+
NfseCertificate.fromBase64(pfxBase64, senha); // aceita quebras de linha e data: URLs
|
|
272
|
+
NfseCertificate.fromPfx(pfxBuffer, senha); // ex.: PFX vindo do seu cofre de segredos
|
|
273
|
+
NfseCertificate.fromPem({ key, cert, passphrase }); // par extraído com openssl
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Vários emitentes (multi-tenant)
|
|
277
|
+
|
|
278
|
+
Crie um `NfseCertificate` por emitente e reaproveite-o. Cada certificado tem o próprio agente mTLS, então conexões nunca são compartilhadas entre emitentes. Guarde o PFX cifrado (KMS, Vault, Secrets Manager) e a senha em um segredo separado, e descriptografe só ao criar o objeto.
|
|
264
279
|
|
|
265
|
-
|
|
280
|
+
```ts
|
|
281
|
+
import { assertCertificateForProvider, NfseCertificate } from '@useinvio/nfse-sdk';
|
|
282
|
+
|
|
283
|
+
const cache = new Map<string, NfseCertificate>();
|
|
284
|
+
|
|
285
|
+
async function certificadoDoEmitente(cnpj: string): Promise<NfseCertificate> {
|
|
286
|
+
const cached = cache.get(cnpj);
|
|
287
|
+
if (cached?.isValidAt()) return cached;
|
|
288
|
+
const { pfx, senha } = await cofre.buscarCertificado(cnpj); // seu secret manager
|
|
289
|
+
const certificado = NfseCertificate.fromPfx(pfx, senha);
|
|
290
|
+
assertCertificateForProvider(certificado, cnpj);
|
|
291
|
+
cache.set(cnpj, certificado);
|
|
292
|
+
return certificado;
|
|
293
|
+
}
|
|
266
294
|
```
|
|
267
295
|
|
|
268
|
-
|
|
296
|
+
### Erros de certificado
|
|
297
|
+
|
|
298
|
+
Todos lançam `CertificateValidationError` com um `code` estável:
|
|
299
|
+
|
|
300
|
+
| `code` | Quando |
|
|
301
|
+
|---|---|
|
|
302
|
+
| `CERTIFICATE_INVALID_PASSWORD` | Senha incorreta. |
|
|
303
|
+
| `CERTIFICATE_UNREADABLE` | O conteúdo não é um PFX/PEM válido. |
|
|
304
|
+
| `CERTIFICATE_MISSING`, `CERTIFICATE_PASSWORD_MISSING` | Variáveis de ambiente ausentes em `fromEnv`. |
|
|
305
|
+
| `CERTIFICATE_NOT_YET_VALID`, `CERTIFICATE_EXPIRED` | Fora da vigência. Verificado antes de assinar e antes de enviar. |
|
|
306
|
+
| `CERTIFICATE_HOLDER_MISMATCH` | `assertCertificateForProvider` com CPF/CNPJ diferente do titular. |
|
|
307
|
+
|
|
308
|
+
> `loadPfx` e `loadPfxFromBuffer` continuam funcionando, mas retornam a chave privada em PEM dentro de um objeto comum. Prefira `NfseCertificate`.
|
|
269
309
|
|
|
270
310
|
---
|
|
271
311
|
|
|
@@ -274,11 +314,11 @@ O objeto `pfx` retornado contém chave privada e certificado em PEM, pronto para
|
|
|
274
314
|
Use quando outro sistema (ERP, sistema legado) já gera o XML da DPS no layout nacional.
|
|
275
315
|
|
|
276
316
|
```ts
|
|
277
|
-
import { emitirNfse,
|
|
317
|
+
import { emitirNfse, NfseCertificate } from '@useinvio/nfse-sdk';
|
|
278
318
|
|
|
279
|
-
const
|
|
319
|
+
const certificado = NfseCertificate.fromEnv();
|
|
280
320
|
|
|
281
|
-
const resultado = await emitirNfse(dpsXml,
|
|
321
|
+
const resultado = await emitirNfse(dpsXml, certificado, { ambiente: 'restrita' });
|
|
282
322
|
|
|
283
323
|
console.log(resultado.chaveAcesso);
|
|
284
324
|
console.log(resultado.nfseXml);
|
|
@@ -296,7 +336,7 @@ Para a referência completa de campos aceitos no JSON, consulte [JSON_MAPPING.md
|
|
|
296
336
|
O builder valida formatos e combinações mínimas antes de gerar XML, mas não escolhe códigos fiscais por você. Campos como `tributacaoMunicipal` e `totTrib` precisam ser informados explicita[...]
|
|
297
337
|
|
|
298
338
|
```ts
|
|
299
|
-
import { emitirNfse,
|
|
339
|
+
import { emitirNfse, NfseCertificate, type DpsJsonRequest } from '@useinvio/nfse-sdk';
|
|
300
340
|
|
|
301
341
|
const nota: DpsJsonRequest = {
|
|
302
342
|
ambiente: 'restrita',
|
|
@@ -333,8 +373,8 @@ const nota: DpsJsonRequest = {
|
|
|
333
373
|
},
|
|
334
374
|
};
|
|
335
375
|
|
|
336
|
-
const
|
|
337
|
-
const resultado = await emitirNfse(nota,
|
|
376
|
+
const certificado = NfseCertificate.fromEnv();
|
|
377
|
+
const resultado = await emitirNfse(nota, certificado);
|
|
338
378
|
```
|
|
339
379
|
|
|
340
380
|
> `pTotTribFed`, `pTotTribEst` e `pTotTribMun` são percentuais com duas casas decimais (ex.: `"5.00"`), diferentes das alíquotas com quatro casas (`pAliq`).
|
|
@@ -365,7 +405,7 @@ console.log(xml); // XML não assinado da DPS
|
|
|
365
405
|
import { EmitirNotaError, emitirNfse } from '@useinvio/nfse-sdk';
|
|
366
406
|
|
|
367
407
|
try {
|
|
368
|
-
await emitirNfse(nota,
|
|
408
|
+
await emitirNfse(nota, certificado);
|
|
369
409
|
} catch (error) {
|
|
370
410
|
if (!(error instanceof EmitirNotaError)) throw error;
|
|
371
411
|
|
|
@@ -387,7 +427,7 @@ try {
|
|
|
387
427
|
```ts
|
|
388
428
|
import { consultarNfse } from '@useinvio/nfse-sdk';
|
|
389
429
|
|
|
390
|
-
const resposta = await consultarNfse(chaveAcesso,
|
|
430
|
+
const resposta = await consultarNfse(chaveAcesso, certificado, 'restrita');
|
|
391
431
|
|
|
392
432
|
if (resposta.status === 200) {
|
|
393
433
|
console.log(resposta.body);
|
|
@@ -403,10 +443,10 @@ if (resposta.status === 200) {
|
|
|
403
443
|
```ts
|
|
404
444
|
import { enviarEvento, gzipBase64, signEnveloped } from '@useinvio/nfse-sdk';
|
|
405
445
|
|
|
406
|
-
const signedEventXml = signEnveloped(pedRegXml, pedRegId, 'infPedReg',
|
|
446
|
+
const signedEventXml = signEnveloped(pedRegXml, pedRegId, 'infPedReg', certificado);
|
|
407
447
|
const pedRegXmlGZipB64 = gzipBase64(signedEventXml);
|
|
408
448
|
|
|
409
|
-
const resposta = await enviarEvento(pedRegXmlGZipB64,
|
|
449
|
+
const resposta = await enviarEvento(pedRegXmlGZipB64, certificado, chaveAcesso, 'restrita');
|
|
410
450
|
```
|
|
411
451
|
|
|
412
452
|
---
|
|
@@ -463,6 +503,34 @@ Leia o guia completo em [docs/METRICS.md](./docs/METRICS.md).
|
|
|
463
503
|
|
|
464
504
|
---
|
|
465
505
|
|
|
506
|
+
## Agentes de IA e MCP
|
|
507
|
+
|
|
508
|
+
O subpath `@useinvio/nfse-sdk/agent` expõe ferramentas neutras de framework para servidores MCP e agentes (OpenAI, LangChain, Vercel AI SDK). Cada ferramenta traz `name`, `description`, `inputSchema`/`outputSchema` em JSON Schema, `annotations` no formato MCP e um handler `run`.
|
|
509
|
+
|
|
510
|
+
| Ferramenta | Efeito | Certificado |
|
|
511
|
+
| --- | --- | --- |
|
|
512
|
+
| `validate_dps` | Valida a requisição; nenhum efeito colateral. | Não |
|
|
513
|
+
| `build_dps_xml` | Gera o XML não assinado e confere contra o XSD v1.01. | Não |
|
|
514
|
+
| `get_nfse` | Consulta uma NFS-e pela chave de acesso. | Sim |
|
|
515
|
+
|
|
516
|
+
```ts
|
|
517
|
+
import { nfseAgentTools } from '@useinvio/nfse-sdk/agent';
|
|
518
|
+
|
|
519
|
+
const context = { pfx: certificado, ambiente: 'restrita' as const }; // configuração do host
|
|
520
|
+
for (const tool of nfseAgentTools) {
|
|
521
|
+
// registre tool.name, tool.description, tool.inputSchema e tool.annotations no seu framework
|
|
522
|
+
// e chame tool.run(argumentos, context) quando o modelo usar a ferramenta
|
|
523
|
+
}
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
Certificado e ambiente ficam no `context` do host, nunca na entrada da ferramenta: o modelo não escolhe `producao` nem vê material de chave. Uma requisição com `ambiente` diferente do configurado gera `AMBIENTE_NOT_ALLOWED`. Falhas esperadas retornam `issues` com `code`, `path`, `severity`, `source` e `suggestion`; falhas de infraestrutura (rede, motor XSD) rejeitam a promise. `toAgentIssues(error)` converte erros do SDK (`DpsFiscalValidationError`, `DpsXsdValidationError`, `CertificateValidationError`, `EmitirNotaError`) para esse mesmo formato.
|
|
527
|
+
|
|
528
|
+
`DPS_JSON_REQUEST_SCHEMA`, exportado também pela raiz do pacote, descreve o `DpsJsonRequest` canônico em JSON Schema 2020-12 com um subconjunto portável de palavras-chave. Ele cobre formatos e domínios de códigos, mas não as regras entre campos (regime × `totTrib`, nacional × exportação); `validateDpsJsonRequest` continua sendo a autoridade. Atalhos legados (`emissao.vServ`, `comExt`) e campos rejeitados pela validação ficam fora do schema; `validate_dps` aponta campos desconhecidos como `UNSUPPORTED_FIELD` em vez de ignorá-los.
|
|
529
|
+
|
|
530
|
+
A emissão ainda não é exposta como ferramenta: ela exigirá confirmação explícita antes do envio.
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
466
534
|
## Referência da API
|
|
467
535
|
|
|
468
536
|
### Funções principais
|
|
@@ -470,9 +538,9 @@ Leia o guia completo em [docs/METRICS.md](./docs/METRICS.md).
|
|
|
470
538
|
| Função | Descrição |
|
|
471
539
|
|---|---|
|
|
472
540
|
| `new NfseClient(options)` | Cria cliente com ambiente, certificado e defaults no mesmo formato do JSON declarativo. |
|
|
473
|
-
| `emitirNfse(input,
|
|
474
|
-
| `consultarNfse(chaveAcesso,
|
|
475
|
-
| `enviarEvento(xmlGzipB64,
|
|
541
|
+
| `emitirNfse(input, certificado, options?)` | Emite uma NFS-e a partir de XML ou JSON. Retorna `ResultadoEmissaoNota`. |
|
|
542
|
+
| `consultarNfse(chaveAcesso, certificado, ambiente?)` | Consulta uma NFS-e pela chave de acesso. |
|
|
543
|
+
| `enviarEvento(xmlGzipB64, certificado, chaveAcesso, ambiente?)` | Envia evento fiscal (cancelamento, etc.). |
|
|
476
544
|
| `buildDpsFromJson(nota)` | Monta o XML da DPS sem assinar nem enviar. |
|
|
477
545
|
| `setSefinRequestObserver(observer)` | Registra um hook para observar latência/status das chamadas à SEFIN. |
|
|
478
546
|
| `createSefinLatencyTracker(options?)` | Agrega amostras recentes e pode calcular percentis quando `includePercentiles` estiver habilitado. |
|
|
@@ -481,17 +549,18 @@ Leia o guia completo em [docs/METRICS.md](./docs/METRICS.md).
|
|
|
481
549
|
|
|
482
550
|
| Função | Descrição |
|
|
483
551
|
|---|---|
|
|
484
|
-
| `assertCertificateValidity(
|
|
485
|
-
| `assertCertificateForProvider(
|
|
486
|
-
| `
|
|
487
|
-
| `
|
|
552
|
+
| `assertCertificateValidity(certificado, now?)` | Verifica vigencia do certificado; aplicada na preparacao e no envio. |
|
|
553
|
+
| `assertCertificateForProvider(certificado, documento, now?)` | Verifica explicitamente vigencia e correspondencia do titular. |
|
|
554
|
+
| `NfseCertificate.fromEnv()` / `fromFile()` / `fromBase64()` / `fromPfx()` / `fromPem()` | Carrega o certificado A1 sem expor a chave privada. |
|
|
555
|
+
| `loadPfx(path, password)` | Legado: retorna a chave em PEM. Prefira `NfseCertificate`. |
|
|
556
|
+
| `loadPfxFromBuffer(buffer, password)` | Legado: retorna a chave em PEM. Prefira `NfseCertificate`. |
|
|
488
557
|
|
|
489
558
|
### Funções de baixo nível
|
|
490
559
|
|
|
491
560
|
| Função | Descrição |
|
|
492
561
|
|---|---|
|
|
493
|
-
| `signDps(xml, dpsId,
|
|
494
|
-
| `signEnveloped(xml, id, ref,
|
|
562
|
+
| `signDps(xml, dpsId, certificado)` | Assina XML de DPS com XMLDSIG. |
|
|
563
|
+
| `signEnveloped(xml, id, ref, certificado)` | Assina XML genérico (eventos). |
|
|
495
564
|
| `verifyDps(xml, certPem)` | Verifica assinatura de uma DPS. |
|
|
496
565
|
| `gzipBase64(xml)` | Compacta XML em GZip e codifica em Base64. |
|
|
497
566
|
| `gunzipBase64(b64)` | Descompacta resposta GZip/Base64. |
|
|
@@ -537,7 +606,7 @@ npm run build # compila para dist/
|
|
|
537
606
|
npm test # build + tipos + testes unitários
|
|
538
607
|
```
|
|
539
608
|
|
|
540
|
-
`npm test` também valida
|
|
609
|
+
`npm test` também valida as DPS geradas contra os XSD oficiais em `schemas/nfse/v1.01/Schemas/1.01` com o motor embutido (libxml2 em WebAssembly). Se o `xmllint` estiver instalado, como no CI, os testes também o usam como verificação cruzada.
|
|
541
610
|
|
|
542
611
|
### Uso local em outro projeto
|
|
543
612
|
|
|
@@ -567,19 +636,9 @@ O pacote usa `publishConfig.access = public`, necessário para publicar um pacot
|
|
|
567
636
|
|
|
568
637
|
---
|
|
569
638
|
|
|
570
|
-
##
|
|
571
|
-
|
|
572
|
-
- Exemplos copiáveis ficam em [`examples/`](./examples).
|
|
573
|
-
- Material de lançamento, mensagens para redes e prompt de landing page ficam em [`docs/launch.md`](./docs/launch.md).
|
|
574
|
-
|
|
575
|
-
---
|
|
576
|
-
|
|
577
|
-
## Assistente GPT
|
|
578
|
-
|
|
579
|
-
Para criar um GPT que ajude outros devs a implementar e documentar esta SDK, use:
|
|
639
|
+
## Exemplos
|
|
580
640
|
|
|
581
|
-
|
|
582
|
-
- [`docs/gpt-knowledge.md`](./docs/gpt-knowledge.md): resumo compacto para subir como arquivo de Knowledge.
|
|
641
|
+
Exemplos copiáveis ficam em [`examples/`](./examples).
|
|
583
642
|
|
|
584
643
|
---
|
|
585
644
|
|
package/dist/agent.d.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { type JsonSchema } from './dpsJsonSchema.js';
|
|
2
|
+
import type { Ambiente } from './config.js';
|
|
3
|
+
import type { DpsJsonRequest } from './dpsJson.js';
|
|
4
|
+
import type { CertificateInput } from './certificate.js';
|
|
5
|
+
export { DPS_JSON_REQUEST_SCHEMA } from './dpsJsonSchema.js';
|
|
6
|
+
export type { JsonSchema } from './dpsJsonSchema.js';
|
|
7
|
+
export type NfseAgentIssueSource = 'sdk' | 'invio' | 'schema' | 'xsd' | 'sefin' | 'certificate' | 'environment';
|
|
8
|
+
/** One shape for validation, XSD, certificate and SEFIN problems. */
|
|
9
|
+
export interface NfseAgentIssue {
|
|
10
|
+
code: string;
|
|
11
|
+
/** Dotted path inside the request (`emissao.` omitted, as in `DpsValidationIssue`); `$` when global. */
|
|
12
|
+
path: string;
|
|
13
|
+
severity: 'error' | 'warning';
|
|
14
|
+
source: NfseAgentIssueSource;
|
|
15
|
+
message: string;
|
|
16
|
+
suggestion?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface NfseAgentContext {
|
|
19
|
+
/** Host-owned A1 certificate (`NfseCertificate` recommended). Required only by tools that talk to SEFIN. */
|
|
20
|
+
pfx?: CertificateInput;
|
|
21
|
+
/** Host-owned environment. Defaults to `restrita`; tool input cannot override it. */
|
|
22
|
+
ambiente?: Ambiente;
|
|
23
|
+
}
|
|
24
|
+
/** Same hints as MCP `ToolAnnotations`. */
|
|
25
|
+
export interface NfseAgentToolAnnotations {
|
|
26
|
+
readOnlyHint: boolean;
|
|
27
|
+
destructiveHint: boolean;
|
|
28
|
+
idempotentHint: boolean;
|
|
29
|
+
openWorldHint: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface NfseAgentTool<Input = any, Output = any> {
|
|
32
|
+
name: string;
|
|
33
|
+
title: string;
|
|
34
|
+
description: string;
|
|
35
|
+
inputSchema: JsonSchema;
|
|
36
|
+
outputSchema: JsonSchema;
|
|
37
|
+
annotations: NfseAgentToolAnnotations;
|
|
38
|
+
requiresCertificate: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Expected failures (invalid input, rejection, missing certificate) resolve
|
|
41
|
+
* with `issues`. Infrastructure failures (XSD engine, network) reject.
|
|
42
|
+
*/
|
|
43
|
+
run(input: Input, context?: NfseAgentContext): Promise<Output>;
|
|
44
|
+
}
|
|
45
|
+
export interface DpsRequestToolInput {
|
|
46
|
+
request: Omit<DpsJsonRequest, 'ambiente'>;
|
|
47
|
+
}
|
|
48
|
+
export interface ValidateDpsToolOutput {
|
|
49
|
+
valid: boolean;
|
|
50
|
+
schemaVersion: string;
|
|
51
|
+
ambiente: Ambiente;
|
|
52
|
+
issues: NfseAgentIssue[];
|
|
53
|
+
warnings: NfseAgentIssue[];
|
|
54
|
+
}
|
|
55
|
+
export interface BuildDpsXmlToolOutput {
|
|
56
|
+
ok: boolean;
|
|
57
|
+
ambiente: Ambiente;
|
|
58
|
+
dpsId?: string;
|
|
59
|
+
xml?: string;
|
|
60
|
+
issues: NfseAgentIssue[];
|
|
61
|
+
warnings: NfseAgentIssue[];
|
|
62
|
+
}
|
|
63
|
+
export interface GetNfseToolInput {
|
|
64
|
+
chaveAcesso: string;
|
|
65
|
+
}
|
|
66
|
+
export interface GetNfseToolOutput {
|
|
67
|
+
ok: boolean;
|
|
68
|
+
ambiente: Ambiente;
|
|
69
|
+
chaveAcesso: string;
|
|
70
|
+
status?: number;
|
|
71
|
+
nfseXml?: string;
|
|
72
|
+
body?: unknown;
|
|
73
|
+
issues: NfseAgentIssue[];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Converts known SDK errors to `NfseAgentIssue[]`. Returns `undefined` for
|
|
77
|
+
* anything else (transport, XSD engine, programming errors) so the caller can rethrow.
|
|
78
|
+
*/
|
|
79
|
+
export declare function toAgentIssues(error: unknown): NfseAgentIssue[] | undefined;
|
|
80
|
+
/** `DPS_JSON_REQUEST_SCHEMA` without `ambiente`, which belongs to the host. */
|
|
81
|
+
export declare const DPS_AGENT_REQUEST_SCHEMA: JsonSchema;
|
|
82
|
+
export declare const validateDpsTool: NfseAgentTool<DpsRequestToolInput, ValidateDpsToolOutput>;
|
|
83
|
+
export declare const buildDpsXmlTool: NfseAgentTool<DpsRequestToolInput, BuildDpsXmlToolOutput>;
|
|
84
|
+
export declare const getNfseTool: NfseAgentTool<GetNfseToolInput, GetNfseToolOutput>;
|
|
85
|
+
/** All tools, in the order an agent typically needs them. */
|
|
86
|
+
export declare const nfseAgentTools: readonly NfseAgentTool[];
|
package/dist/agent.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-neutral tool definitions for MCP servers and LLM agents.
|
|
3
|
+
*
|
|
4
|
+
* Each tool carries a JSON Schema for input and output, MCP-style annotations
|
|
5
|
+
* and a `run` handler. Adapters (MCP, OpenAI, LangChain, Vercel AI SDK) only
|
|
6
|
+
* need to map these fields. Certificate and environment are host
|
|
7
|
+
* configuration passed in `NfseAgentContext`, never tool input, so a model
|
|
8
|
+
* cannot select production or see key material.
|
|
9
|
+
*/
|
|
10
|
+
import { CertificateValidationError } from './certificateValidation.js';
|
|
11
|
+
import { DEFAULT_AMBIENTE } from './config.js';
|
|
12
|
+
import { buildDpsFromJson } from './dpsJson.js';
|
|
13
|
+
import { DPS_JSON_REQUEST_SCHEMA } from './dpsJsonSchema.js';
|
|
14
|
+
import { EmitirNotaError } from './emissaoNota.js';
|
|
15
|
+
import { DpsFiscalValidationError, validateDpsJsonRequest } from './fiscalValidation.js';
|
|
16
|
+
import { gunzipBase64 } from './gzipB64.js';
|
|
17
|
+
import { checkJsonSchema } from './jsonSchemaCheck.js';
|
|
18
|
+
import { consultarNfse, extrairErros } from './sefinClient.js';
|
|
19
|
+
import { DpsXsdValidationError, validateDpsXmlAgainstXsd } from './xsdValidation.js';
|
|
20
|
+
export { DPS_JSON_REQUEST_SCHEMA } from './dpsJsonSchema.js';
|
|
21
|
+
function fromValidationIssue(issue) {
|
|
22
|
+
return { ...issue };
|
|
23
|
+
}
|
|
24
|
+
function sefinIssue(erro) {
|
|
25
|
+
const authorization = erro.Codigo === 'INVALID_AUTHORIZATION_RESPONSE';
|
|
26
|
+
return {
|
|
27
|
+
code: erro.Codigo,
|
|
28
|
+
path: '$',
|
|
29
|
+
severity: 'error',
|
|
30
|
+
source: authorization ? 'sdk' : 'sefin',
|
|
31
|
+
message: erro.Complemento ? `${erro.Descricao} (${erro.Complemento})` : erro.Descricao,
|
|
32
|
+
...(authorization ? { suggestion: 'Consulte a nota pela chave ou concilie a DPS antes de reenviar.' } : {}),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Converts known SDK errors to `NfseAgentIssue[]`. Returns `undefined` for
|
|
37
|
+
* anything else (transport, XSD engine, programming errors) so the caller can rethrow.
|
|
38
|
+
*/
|
|
39
|
+
export function toAgentIssues(error) {
|
|
40
|
+
if (error instanceof DpsFiscalValidationError)
|
|
41
|
+
return error.issues.map(fromValidationIssue);
|
|
42
|
+
if (error instanceof DpsXsdValidationError) {
|
|
43
|
+
return error.details.map((message) => ({ code: 'XSD_INVALID', path: '$', severity: 'error', source: 'xsd', message }));
|
|
44
|
+
}
|
|
45
|
+
if (error instanceof CertificateValidationError) {
|
|
46
|
+
return [{ code: error.code, path: '$', severity: 'error', source: 'certificate', message: error.message }];
|
|
47
|
+
}
|
|
48
|
+
if (error instanceof EmitirNotaError) {
|
|
49
|
+
const issues = error.erros.map(sefinIssue);
|
|
50
|
+
return issues.length ? issues : [{ code: 'SEFIN_REJECTED', path: '$', severity: 'error', source: 'sefin', message: error.message }];
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
function resolveAmbiente(context) {
|
|
55
|
+
return context?.ambiente ?? DEFAULT_AMBIENTE;
|
|
56
|
+
}
|
|
57
|
+
function stripEmissao(path) {
|
|
58
|
+
return path.replace(/^emissao\./, '');
|
|
59
|
+
}
|
|
60
|
+
/** Fields the schema does not accept are reported instead of silently ignored by the builder. */
|
|
61
|
+
function unsupportedFieldIssues(request) {
|
|
62
|
+
return checkJsonSchema(DPS_JSON_REQUEST_SCHEMA, request)
|
|
63
|
+
.filter(({ keyword }) => keyword === 'additionalProperties')
|
|
64
|
+
.map(({ path }) => ({
|
|
65
|
+
code: 'UNSUPPORTED_FIELD',
|
|
66
|
+
path: stripEmissao(path),
|
|
67
|
+
severity: 'error',
|
|
68
|
+
source: 'schema',
|
|
69
|
+
message: `${path} nao e um campo suportado pelo schema da DPS`,
|
|
70
|
+
suggestion: 'Remova o campo ou use o nome canonico descrito no schema.',
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
function environmentIssue(request, ambiente) {
|
|
74
|
+
const requested = request?.ambiente;
|
|
75
|
+
if (requested === undefined || requested === ambiente)
|
|
76
|
+
return [];
|
|
77
|
+
return [{
|
|
78
|
+
code: 'AMBIENTE_NOT_ALLOWED',
|
|
79
|
+
path: 'ambiente',
|
|
80
|
+
severity: 'error',
|
|
81
|
+
source: 'environment',
|
|
82
|
+
message: `O ambiente e definido pelo host (${ambiente}); a requisicao pediu ${String(requested)}.`,
|
|
83
|
+
suggestion: 'Remova o campo ambiente da requisicao.',
|
|
84
|
+
}];
|
|
85
|
+
}
|
|
86
|
+
function validateForAgent(input, context) {
|
|
87
|
+
const ambiente = resolveAmbiente(context);
|
|
88
|
+
const request = input?.request;
|
|
89
|
+
const hostIssues = environmentIssue(request, ambiente);
|
|
90
|
+
const withAmbiente = request && typeof request === 'object' && !Array.isArray(request) ? { ...request, ambiente } : request;
|
|
91
|
+
const report = validateDpsJsonRequest(withAmbiente);
|
|
92
|
+
const structural = report.issues.some((issue) => issue.code === 'INVALID_STRUCTURE');
|
|
93
|
+
const issues = [
|
|
94
|
+
...hostIssues,
|
|
95
|
+
...(structural ? [] : unsupportedFieldIssues(withAmbiente)),
|
|
96
|
+
...report.issues.map(fromValidationIssue),
|
|
97
|
+
];
|
|
98
|
+
return { ambiente, report, issues, warnings: report.warnings.map(fromValidationIssue), request: withAmbiente };
|
|
99
|
+
}
|
|
100
|
+
const { $schema: _draft, ...requestSchema } = DPS_JSON_REQUEST_SCHEMA;
|
|
101
|
+
const { ambiente: _ambiente, ...requestProperties } = requestSchema.properties;
|
|
102
|
+
/** `DPS_JSON_REQUEST_SCHEMA` without `ambiente`, which belongs to the host. */
|
|
103
|
+
export const DPS_AGENT_REQUEST_SCHEMA = { ...requestSchema, properties: requestProperties };
|
|
104
|
+
const DPS_REQUEST_INPUT_SCHEMA = {
|
|
105
|
+
type: 'object',
|
|
106
|
+
properties: { request: DPS_AGENT_REQUEST_SCHEMA },
|
|
107
|
+
required: ['request'],
|
|
108
|
+
additionalProperties: false,
|
|
109
|
+
};
|
|
110
|
+
const ISSUE_SCHEMA = {
|
|
111
|
+
type: 'object',
|
|
112
|
+
properties: {
|
|
113
|
+
code: { type: 'string', description: 'Codigo estavel do problema, ex.: TOT_TRIB_REQUIRED ou codigo SEFIN.' },
|
|
114
|
+
path: { type: 'string', description: 'Campo afetado; $ quando global.' },
|
|
115
|
+
severity: { type: 'string', enum: ['error', 'warning'] },
|
|
116
|
+
source: { type: 'string', enum: ['sdk', 'invio', 'schema', 'xsd', 'sefin', 'certificate', 'environment'] },
|
|
117
|
+
message: { type: 'string' },
|
|
118
|
+
suggestion: { type: 'string' },
|
|
119
|
+
},
|
|
120
|
+
required: ['code', 'path', 'severity', 'source', 'message'],
|
|
121
|
+
additionalProperties: false,
|
|
122
|
+
};
|
|
123
|
+
const ISSUES = { type: 'array', items: ISSUE_SCHEMA };
|
|
124
|
+
const AMBIENTE = { type: 'string', enum: ['restrita', 'producao'] };
|
|
125
|
+
export const validateDpsTool = {
|
|
126
|
+
name: 'validate_dps',
|
|
127
|
+
title: 'Validar DPS da NFS-e',
|
|
128
|
+
description: 'Valida uma requisicao de DPS (NFS-e Nacional) sem certificado, sem rede e sem efeitos colaterais. ' +
|
|
129
|
+
'Retorna issues com code, path, message e suggestion; corrija os campos indicados e valide novamente ate valid=true. ' +
|
|
130
|
+
'Nao invente codigos fiscais (cTribNac, regime, tributacao): pergunte ao usuario quando faltarem.',
|
|
131
|
+
inputSchema: DPS_REQUEST_INPUT_SCHEMA,
|
|
132
|
+
outputSchema: {
|
|
133
|
+
type: 'object',
|
|
134
|
+
properties: { valid: { type: 'boolean' }, schemaVersion: { type: 'string' }, ambiente: AMBIENTE, issues: ISSUES, warnings: ISSUES },
|
|
135
|
+
required: ['valid', 'schemaVersion', 'ambiente', 'issues', 'warnings'],
|
|
136
|
+
additionalProperties: false,
|
|
137
|
+
},
|
|
138
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
139
|
+
requiresCertificate: false,
|
|
140
|
+
async run(input, context) {
|
|
141
|
+
const { ambiente, report, issues, warnings } = validateForAgent(input, context);
|
|
142
|
+
return { valid: issues.length === 0, schemaVersion: report.schemaVersion, ambiente, issues, warnings };
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
export const buildDpsXmlTool = {
|
|
146
|
+
name: 'build_dps_xml',
|
|
147
|
+
title: 'Gerar XML da DPS',
|
|
148
|
+
description: 'Valida a requisicao e gera o XML nao assinado da DPS, conferido contra o XSD oficial v1.01. ' +
|
|
149
|
+
'Nao assina nem transmite. Use para revisar a nota antes de pedir a emissao ao usuario.',
|
|
150
|
+
inputSchema: DPS_REQUEST_INPUT_SCHEMA,
|
|
151
|
+
outputSchema: {
|
|
152
|
+
type: 'object',
|
|
153
|
+
properties: {
|
|
154
|
+
ok: { type: 'boolean' },
|
|
155
|
+
ambiente: AMBIENTE,
|
|
156
|
+
dpsId: { type: 'string', description: 'Id de 45 caracteres da DPS.' },
|
|
157
|
+
xml: { type: 'string', description: 'XML da DPS nao assinado.' },
|
|
158
|
+
issues: ISSUES,
|
|
159
|
+
warnings: ISSUES,
|
|
160
|
+
},
|
|
161
|
+
required: ['ok', 'ambiente', 'issues', 'warnings'],
|
|
162
|
+
additionalProperties: false,
|
|
163
|
+
},
|
|
164
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
165
|
+
requiresCertificate: false,
|
|
166
|
+
async run(input, context) {
|
|
167
|
+
const { ambiente, issues, warnings, request } = validateForAgent(input, context);
|
|
168
|
+
if (issues.length)
|
|
169
|
+
return { ok: false, ambiente, issues, warnings };
|
|
170
|
+
try {
|
|
171
|
+
const built = buildDpsFromJson(request);
|
|
172
|
+
validateDpsXmlAgainstXsd(built.xml);
|
|
173
|
+
return { ok: true, ambiente, dpsId: built.id, xml: built.xml, issues: [], warnings };
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
const mapped = toAgentIssues(error);
|
|
177
|
+
if (!mapped)
|
|
178
|
+
throw error;
|
|
179
|
+
return { ok: false, ambiente, issues: mapped, warnings };
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
export const getNfseTool = {
|
|
184
|
+
name: 'get_nfse',
|
|
185
|
+
title: 'Consultar NFS-e',
|
|
186
|
+
description: 'Consulta uma NFS-e na SEFIN pela chave de acesso de 50 digitos, no ambiente configurado pelo host. ' +
|
|
187
|
+
'Use tambem para conciliar uma emissao com resposta ambigua antes de qualquer reenvio.',
|
|
188
|
+
inputSchema: {
|
|
189
|
+
type: 'object',
|
|
190
|
+
properties: { chaveAcesso: { type: 'string', pattern: '^\\d{50}$', description: 'Chave de acesso da NFS-e, 50 digitos.' } },
|
|
191
|
+
required: ['chaveAcesso'],
|
|
192
|
+
additionalProperties: false,
|
|
193
|
+
},
|
|
194
|
+
outputSchema: {
|
|
195
|
+
type: 'object',
|
|
196
|
+
properties: {
|
|
197
|
+
ok: { type: 'boolean' },
|
|
198
|
+
ambiente: AMBIENTE,
|
|
199
|
+
chaveAcesso: { type: 'string' },
|
|
200
|
+
status: { type: 'number', description: 'Status HTTP da SEFIN.' },
|
|
201
|
+
nfseXml: { type: 'string', description: 'XML da NFS-e quando retornado.' },
|
|
202
|
+
body: { description: 'Corpo da resposta da SEFIN, sem o XML compactado.' },
|
|
203
|
+
issues: ISSUES,
|
|
204
|
+
},
|
|
205
|
+
required: ['ok', 'ambiente', 'chaveAcesso', 'issues'],
|
|
206
|
+
additionalProperties: false,
|
|
207
|
+
},
|
|
208
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
209
|
+
requiresCertificate: true,
|
|
210
|
+
async run(input, context) {
|
|
211
|
+
const ambiente = resolveAmbiente(context);
|
|
212
|
+
const chaveAcesso = String(input?.chaveAcesso ?? '');
|
|
213
|
+
const fail = (issue) => ({ ok: false, ambiente, chaveAcesso, issues: [issue] });
|
|
214
|
+
if (!/^\d{50}$/.test(chaveAcesso)) {
|
|
215
|
+
return fail({ code: 'CHAVE_ACESSO_INVALID_FORMAT', path: 'chaveAcesso', severity: 'error', source: 'sdk', message: 'chaveAcesso deve ter 50 digitos numericos.' });
|
|
216
|
+
}
|
|
217
|
+
if (!context?.pfx) {
|
|
218
|
+
return fail({ code: 'CERTIFICATE_REQUIRED', path: '$', severity: 'error', source: 'certificate', message: 'O host nao configurou um certificado A1 para consultar a SEFIN.' });
|
|
219
|
+
}
|
|
220
|
+
const { status, body } = await consultarNfse(chaveAcesso, context.pfx, ambiente);
|
|
221
|
+
const { nfseXmlGZipB64, NfseXmlGZipB64, ...rest } = (body && typeof body === 'object' && !Array.isArray(body) ? body : { value: body });
|
|
222
|
+
const compressed = nfseXmlGZipB64 ?? NfseXmlGZipB64;
|
|
223
|
+
if (status < 200 || status >= 300) {
|
|
224
|
+
const issues = extrairErros(body).map(sefinIssue);
|
|
225
|
+
return { ok: false, ambiente, chaveAcesso, status, body: rest, issues: issues.length ? issues : [
|
|
226
|
+
{ code: `HTTP_${status}`, path: '$', severity: 'error', source: 'sefin', message: `A SEFIN respondeu HTTP ${status}.` },
|
|
227
|
+
] };
|
|
228
|
+
}
|
|
229
|
+
const nfseXml = typeof compressed === 'string' ? gunzipBase64(compressed) : undefined;
|
|
230
|
+
return { ok: true, ambiente, chaveAcesso, status, ...(nfseXml ? { nfseXml } : {}), body: rest, issues: [] };
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
/** All tools, in the order an agent typically needs them. */
|
|
234
|
+
export const nfseAgentTools = [validateDpsTool, buildDpsXmlTool, getNfseTool];
|
|
235
|
+
//# sourceMappingURL=agent.js.map
|