@comando.one/mcp-server 0.5.0 → 0.6.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 +1 -1
- package/dist/index.js +141 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ Ambos expõem o **mesmo conjunto de ferramentas** (131 curadas + `lookups` + `co
|
|
|
51
51
|
|
|
52
52
|
## Como funciona
|
|
53
53
|
|
|
54
|
-
- **
|
|
54
|
+
- **134 tools curadas** (gerados a partir do OpenAPI canônico, zero drift) cobrindo as operações de maior valor — `customers_list`, `invoices_create`/`invoices_delete`, `charges_create`, `nfse_emit`, `payouts_create`, `contracts_pause`/`contracts_resume`, `service_categories_create`, `reports_aging_actions`/`reports_cashflow_forecast`, `audit_timeline`, `documents_analyze` (lê boleto/guia/NF anexada e extrai valor, vencimento e emitente), `cost_centers_create`, `invoices_settle_charge` (dá baixa numa parcela — registra o recebimento), etc.
|
|
55
55
|
- **`comando_request`** — escape hatch genérico (`method`, `path`, `query`, `body`) que cobre 100% dos endpoints restantes.
|
|
56
56
|
- **`lookups`** — catálogos read-only (centros de custo, condições/métodos de pagamento, naturezas, contas bancárias).
|
|
57
57
|
- **`whoami`** — identidade, empresas acessíveis e scopes da chave.
|
package/dist/index.js
CHANGED
|
@@ -144,6 +144,9 @@ var MANIFEST = [
|
|
|
144
144
|
{ operationId: "createInvoice", tool: "invoices_create", write: true },
|
|
145
145
|
{ operationId: "cancelInvoice", tool: "invoices_cancel", write: true, destructive: true },
|
|
146
146
|
{ operationId: "sendInvoiceSecondCopy", tool: "invoices_send", write: true },
|
|
147
|
+
{ operationId: "setInvoiceAutoSend", tool: "invoices_set_auto_send", write: true },
|
|
148
|
+
{ operationId: "listInvoiceCharges", tool: "invoices_list_charges" },
|
|
149
|
+
{ operationId: "settleInvoiceCharge", tool: "invoices_settle_charge", write: true },
|
|
147
150
|
{ operationId: "generateContractInvoices", tool: "contracts_generate_invoices", write: true },
|
|
148
151
|
// ----- Cobranças -----
|
|
149
152
|
{ operationId: "listCharges", tool: "charges_list" },
|
|
@@ -1154,13 +1157,13 @@ var SCHEMAS = {
|
|
|
1154
1157
|
},
|
|
1155
1158
|
InvoiceCreate: {
|
|
1156
1159
|
type: "object",
|
|
1157
|
-
required: ["customer_id"],
|
|
1160
|
+
required: ["customer_id", "due_date"],
|
|
1158
1161
|
properties: {
|
|
1159
1162
|
customer_id: { type: "string", format: "uuid" },
|
|
1160
1163
|
title: { type: "string" },
|
|
1161
1164
|
amount: { type: "number", description: "Ignorado se 'items' for enviado (calculado dos itens)" },
|
|
1162
1165
|
status: { type: "string", enum: ["rascunho", "enviada", "aceita", "ativa"], default: "rascunho" },
|
|
1163
|
-
due_date: { type: "string", format: "date" },
|
|
1166
|
+
due_date: { type: "string", format: "date", description: "Obrigat\xF3rio. A fatura nasce com uma parcela 1/1 neste vencimento \u2014 sem parcela ningu\xE9m cobra (o dispatch seleciona por parcela). O envio autom\xE1tico nasce desligado; para ligar use PATCH /v1/invoices/{id}/auto-send." },
|
|
1164
1167
|
items: {
|
|
1165
1168
|
type: "array",
|
|
1166
1169
|
items: {
|
|
@@ -1224,7 +1227,7 @@ var SCHEMAS = {
|
|
|
1224
1227
|
type: "object",
|
|
1225
1228
|
required: ["motivo", "justificativa"],
|
|
1226
1229
|
properties: {
|
|
1227
|
-
motivo: { type: "integer", enum: [1, 2, 3, 4, 9], description: "1=
|
|
1230
|
+
motivo: { type: "integer", enum: [1, 2, 3, 4, 9], description: "1=erro na emiss\xE3o, 2=servi\xE7o n\xE3o prestado, 3=duplicidade de emiss\xE3o, 4=decis\xE3o judicial, 9=outros. Use o c\xF3digo pelo R\xD3TULO, n\xE3o pela ordem." },
|
|
1228
1231
|
justificativa: { type: "string", minLength: 15, maxLength: 255 }
|
|
1229
1232
|
}
|
|
1230
1233
|
},
|
|
@@ -1827,7 +1830,7 @@ var OPENAPI_SPEC = {
|
|
|
1827
1830
|
tags: ["Propostas"],
|
|
1828
1831
|
summary: "Enviar proposta",
|
|
1829
1832
|
operationId: "sendProposal",
|
|
1830
|
-
description: "
|
|
1833
|
+
description: "Apenas MUDA O STATUS da proposta para 'enviada'. N\xC3O dispara e-mail \u2014 n\xE3o existe envio de proposta por e-mail no sistema (conferido: email_schedules s\xF3 tem invoice e charge). Depois de chamar, n\xE3o afirme ao usu\xE1rio que a proposta foi enviada ao cliente.",
|
|
1831
1834
|
security: [{ ApiKeyAuth: ["proposals:send"] }],
|
|
1832
1835
|
responses: { "200": { description: "Proposta enviada", content: { "application/json": { schema: { $ref: "#/components/schemas/Proposal" } } } }, "404": errorResponse("Proposta n\xE3o encontrada"), "409": errorResponse("Proposta cancelada"), ...COMMON_ERRORS }
|
|
1833
1836
|
}
|
|
@@ -2038,7 +2041,8 @@ var OPENAPI_SPEC = {
|
|
|
2038
2041
|
tags: ["Contratos"],
|
|
2039
2042
|
summary: "Gerar as faturas devidas do contrato",
|
|
2040
2043
|
operationId: "generateContractInvoices",
|
|
2041
|
-
description: "Gera
|
|
2044
|
+
description: "Gera fatura do contrato. Por padr\xE3o (mode=extra) cria uma fatura AVULSA fora do ciclo: n\xE3o avan\xE7a o agendamento, ent\xE3o chamar duas vezes gera DUAS faturas. Use mode=cycle para antecipar a fatura do ciclo corrente \u2014 \xE9 esse modo que avan\xE7a next_generation_date e evita duplicar a compet\xEAncia. mode=skip pula o ciclo sem gerar nada. Contrato cancelado ou suspenso \xE9 recusado.",
|
|
2045
|
+
requestBody: { required: false, content: { "application/json": { schema: { type: "object", properties: { mode: { type: "string", enum: ["extra", "cycle", "skip"], default: "extra", description: "extra = avulsa (repet\xEDvel) \xB7 cycle = fatura do ciclo, avan\xE7a o agendamento \xB7 skip = s\xF3 avan\xE7a" } } } } } },
|
|
2042
2046
|
security: [{ ApiKeyAuth: ["invoices:create"] }],
|
|
2043
2047
|
responses: {
|
|
2044
2048
|
"200": {
|
|
@@ -2059,6 +2063,105 @@ var OPENAPI_SPEC = {
|
|
|
2059
2063
|
}
|
|
2060
2064
|
}
|
|
2061
2065
|
},
|
|
2066
|
+
"/invoices/{id}/auto-send": {
|
|
2067
|
+
parameters: [{ name: "id", in: "path", required: true, schema: { type: "string", format: "uuid" } }],
|
|
2068
|
+
patch: {
|
|
2069
|
+
tags: ["Faturas"],
|
|
2070
|
+
summary: "Programar a cobran\xE7a autom\xE1tica",
|
|
2071
|
+
operationId: "setInvoiceAutoSend",
|
|
2072
|
+
description: "Liga ou desliga o envio AUTOM\xC1TICO da cobran\xE7a nas parcelas em aberto da fatura. Sem isso a cobran\xE7a nunca sai sozinha: o disparo di\xE1rio s\xF3 envia com `auto_send_enabled` ligado na parcela, e a fatura criada por API nasce desligada. `days_before` = quantos dias ANTES do vencimento disparar (0 = no dia). Desligar devolve o canal para `manual`.",
|
|
2073
|
+
security: [{ ApiKeyAuth: ["invoices:send"] }],
|
|
2074
|
+
requestBody: { required: true, content: { "application/json": { schema: { type: "object", properties: {
|
|
2075
|
+
enabled: { type: "boolean", default: true, description: "false desliga e volta o canal para manual" },
|
|
2076
|
+
channel: { type: "string", enum: ["manual", "email", "whatsapp", "ambos"], default: "email", description: '"ambos" = e-mail E WhatsApp' },
|
|
2077
|
+
days_before: { type: "integer", minimum: 0, maximum: 90, default: 0, description: "dias ANTES do vencimento" }
|
|
2078
|
+
} } } } },
|
|
2079
|
+
responses: {
|
|
2080
|
+
"200": { description: "Programa\xE7\xE3o aplicada", content: { "application/json": { schema: { type: "object", properties: {
|
|
2081
|
+
invoice_id: { type: "string" },
|
|
2082
|
+
auto_send_enabled: { type: "boolean" },
|
|
2083
|
+
send_channel: { type: "string" },
|
|
2084
|
+
send_days_before: { type: "integer" },
|
|
2085
|
+
parcelas_afetadas: { type: "integer" }
|
|
2086
|
+
} } } } },
|
|
2087
|
+
"400": errorResponse("channel ou days_before inv\xE1lidos"),
|
|
2088
|
+
"404": errorResponse("Fatura n\xE3o encontrada"),
|
|
2089
|
+
"409": errorResponse("Fatura sem parcela em aberto"),
|
|
2090
|
+
...COMMON_ERRORS
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
},
|
|
2094
|
+
"/invoices/{id}/charges": {
|
|
2095
|
+
parameters: [{ name: "id", in: "path", required: true, schema: { type: "string", format: "uuid" } }],
|
|
2096
|
+
get: {
|
|
2097
|
+
tags: ["Faturas"],
|
|
2098
|
+
summary: "Listar as parcelas da fatura",
|
|
2099
|
+
operationId: "listInvoiceCharges",
|
|
2100
|
+
description: "As PARCELAS (invoice_charges) da fatura, com quanto j\xE1 foi pago e quanto falta em cada uma. N\xE3o confunda com `/v1/charges`, que \xE9 a cobran\xE7a do GATEWAY (Pix/boleto emitido no banco): a parcela \xE9 o que manda no vencimento e no status de pagamento da fatura. Use esta rota para descobrir o `charge_id` antes de dar baixa.",
|
|
2101
|
+
security: [{ ApiKeyAuth: ["invoices:read"] }],
|
|
2102
|
+
responses: { "200": { description: "Parcelas da fatura", content: { "application/json": { schema: { type: "object", properties: {
|
|
2103
|
+
invoice_id: { type: "string" },
|
|
2104
|
+
invoice_number: { type: "string" },
|
|
2105
|
+
invoice_status: { type: "string" },
|
|
2106
|
+
invoice_payment_status: { type: "string" },
|
|
2107
|
+
total: { type: "integer" },
|
|
2108
|
+
charges: { type: "array", items: { type: "object", properties: {
|
|
2109
|
+
id: { type: "string" },
|
|
2110
|
+
sequence: { type: "integer" },
|
|
2111
|
+
description: { type: "string" },
|
|
2112
|
+
amount: { type: "number" },
|
|
2113
|
+
due_date: { type: "string", format: "date" },
|
|
2114
|
+
payment_status: { type: "string", enum: ["pendente", "parcial", "pago", "vencido"] },
|
|
2115
|
+
paid_amount: { type: "number", description: "soma j\xE1 baixada nesta parcela" },
|
|
2116
|
+
remaining_amount: { type: "number", description: "saldo a receber" },
|
|
2117
|
+
auto_send_enabled: { type: "boolean" },
|
|
2118
|
+
send_channel: { type: "string" }
|
|
2119
|
+
} } }
|
|
2120
|
+
} } } } }, "404": errorResponse("Fatura n\xE3o encontrada"), ...COMMON_ERRORS }
|
|
2121
|
+
}
|
|
2122
|
+
},
|
|
2123
|
+
"/invoices/{id}/charges/{charge_id}/pay": {
|
|
2124
|
+
parameters: [
|
|
2125
|
+
{ name: "id", in: "path", required: true, schema: { type: "string", format: "uuid" } },
|
|
2126
|
+
{ name: "charge_id", in: "path", required: true, schema: { type: "string", format: "uuid" }, description: "id da PARCELA (veja GET /v1/invoices/{id}/charges)" }
|
|
2127
|
+
],
|
|
2128
|
+
post: {
|
|
2129
|
+
tags: ["Faturas"],
|
|
2130
|
+
summary: "Dar baixa em uma parcela",
|
|
2131
|
+
operationId: "settleInvoiceCharge",
|
|
2132
|
+
description: "Registra o recebimento de uma parcela: cria o pagamento e o aloca \xE0 parcela. A parcela e a fatura passam a `pago` (ou `parcial`) pelo pr\xF3prio banco. Sem `amount`, quita o saldo restante; sem `payment_date`, usa hoje (America/Sao_Paulo). Baixa PARCIAL \xE9 permitida; acima do saldo, n\xE3o. Campo de corpo que a rota n\xE3o conhece devolve 400 em vez de ser ignorado.",
|
|
2133
|
+
security: [{ ApiKeyAuth: ["invoices:settle"] }],
|
|
2134
|
+
requestBody: { required: false, content: { "application/json": { schema: { type: "object", properties: {
|
|
2135
|
+
amount: { type: "number", description: "Valor recebido. Omitido, quita o saldo restante da parcela." },
|
|
2136
|
+
payment_date: { type: "string", format: "date", description: "Data do recebimento (YYYY-MM-DD). Padr\xE3o: hoje." },
|
|
2137
|
+
receipt_method_id: { type: "string", format: "uuid", description: "Forma de RECEBIMENTO (GET /v1/payment-methods). Aceita tamb\xE9m o nome `payment_method_id`." },
|
|
2138
|
+
bank_account_id: { type: "string", format: "uuid", description: "Conta que recebeu (GET /v1/bank-accounts)." },
|
|
2139
|
+
notes: { type: "string", description: "Observa\xE7\xE3o livre do pagamento." }
|
|
2140
|
+
} } } } },
|
|
2141
|
+
responses: {
|
|
2142
|
+
"201": { description: "Baixa registrada", content: { "application/json": { schema: { type: "object", properties: {
|
|
2143
|
+
payment_id: { type: "string" },
|
|
2144
|
+
invoice_id: { type: "string" },
|
|
2145
|
+
invoice_number: { type: "string" },
|
|
2146
|
+
invoice_payment_status: { type: "string" },
|
|
2147
|
+
amount: { type: "number" },
|
|
2148
|
+
payment_date: { type: "string", format: "date" },
|
|
2149
|
+
charge: { type: "object", properties: {
|
|
2150
|
+
id: { type: "string" },
|
|
2151
|
+
sequence: { type: "integer" },
|
|
2152
|
+
amount: { type: "number" },
|
|
2153
|
+
paid_amount: { type: "number" },
|
|
2154
|
+
remaining_amount: { type: "number" },
|
|
2155
|
+
payment_status: { type: "string" }
|
|
2156
|
+
} }
|
|
2157
|
+
} } } } },
|
|
2158
|
+
"400": errorResponse("amount, payment_date ou campo de corpo inv\xE1lido"),
|
|
2159
|
+
"404": errorResponse("Fatura ou parcela n\xE3o encontrada"),
|
|
2160
|
+
"409": errorResponse("Fatura cancelada, parcela j\xE1 quitada ou valor acima do saldo"),
|
|
2161
|
+
...COMMON_ERRORS
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
},
|
|
2062
2165
|
"/invoices/{id}/send": {
|
|
2063
2166
|
parameters: [{ name: "id", in: "path", required: true, schema: { type: "string", format: "uuid" } }],
|
|
2064
2167
|
post: {
|
|
@@ -2510,6 +2613,7 @@ var OPENAPI_SPEC = {
|
|
|
2510
2613
|
{ name: "include_shared", in: "query", schema: { type: "boolean", default: true }, description: "Incluir clientes compartilhados" },
|
|
2511
2614
|
{ name: "customer_id", in: "query", schema: { type: "string", format: "uuid" } },
|
|
2512
2615
|
{ name: "search", in: "query", schema: { type: "string" } },
|
|
2616
|
+
{ name: "company_id", in: "query", schema: { type: "string", format: "uuid" }, description: "Restringe a UMA empresa: a sua, ou a dona de um cliente compartilhado. Aplicado SOBRE o escopo permitido \u2014 nunca amplia acesso." },
|
|
2513
2617
|
{ name: "query", in: "query", schema: { type: "string" }, description: "Apelido de 'search' \u2014 o c\xF3digo j\xE1 aceitava os dois nomes; agora est\xE1 documentado." }
|
|
2514
2618
|
],
|
|
2515
2619
|
responses: { "200": { description: "Faturas em atraso + a\xE7\xF5es recomendadas", content: { "application/json": { schema: { type: "object", properties: {
|
|
@@ -2520,6 +2624,8 @@ var OPENAPI_SPEC = {
|
|
|
2520
2624
|
total_invoiced_amount: { type: "number", description: "Soma do valor cheio das mesmas faturas, ignorando pagamentos parciais." },
|
|
2521
2625
|
actions: { type: "array", items: { type: "object", properties: {
|
|
2522
2626
|
invoice_id: { type: "string", format: "uuid" },
|
|
2627
|
+
company_id: { type: "string", format: "uuid", description: "Empresa DONA da fatura. Com clientes compartilhados a lista mistura empresas; sem este campo n\xE3o d\xE1 para saber de quem \xE9 cada linha." },
|
|
2628
|
+
company_name: { type: "string", nullable: true },
|
|
2523
2629
|
invoice_number: { type: "string", nullable: true },
|
|
2524
2630
|
title: { type: "string", nullable: true },
|
|
2525
2631
|
customer_id: { type: "string", format: "uuid" },
|
|
@@ -2541,6 +2647,7 @@ var OPENAPI_SPEC = {
|
|
|
2541
2647
|
tags: ["Relat\xF3rios"],
|
|
2542
2648
|
summary: "Previs\xE3o de fluxo de caixa",
|
|
2543
2649
|
operationId: "reportCashflowForecast",
|
|
2650
|
+
description: "Projeta o caixa dia a dia pelas parcelas em aberto. `total_inflow`/`total_outflow` e a `timeline` usam o **saldo devedor** de cada parcela (valor menos o que j\xE1 foi baixado) \u2014 \xE9 o que de fato entra e sai; `total_inflow_full`/`total_outflow_full` trazem o valor cheio. Parcela de fatura/compra **cancelada ou em rascunho** n\xE3o entra: o t\xEDtulo foi desfeito ou ainda n\xE3o foi emitido.",
|
|
2544
2651
|
security: [{ ApiKeyAuth: ["finance:read"] }],
|
|
2545
2652
|
parameters: [
|
|
2546
2653
|
{ name: "horizon_days", in: "query", schema: { type: "integer", minimum: 1, maximum: 120, default: 30 } },
|
|
@@ -2548,7 +2655,7 @@ var OPENAPI_SPEC = {
|
|
|
2548
2655
|
{ name: "include_receivables", in: "query", schema: { type: "boolean", default: true } },
|
|
2549
2656
|
{ name: "include_payables", in: "query", schema: { type: "boolean", default: true } }
|
|
2550
2657
|
],
|
|
2551
|
-
responses: { "200": { description: "Proje\xE7\xE3o di\xE1ria de caixa (timeline)", content: { "application/json": { schema: { type: "object" } } } }, ...COMMON_ERRORS }
|
|
2658
|
+
responses: { "200": { description: "Proje\xE7\xE3o di\xE1ria de caixa (timeline). Cada linha de `receivables_preview`/`payables_preview` traz `amount` (cheio), `paid_amount` e `outstanding_amount` (o que falta).", content: { "application/json": { schema: { type: "object" } } } }, ...COMMON_ERRORS }
|
|
2552
2659
|
}
|
|
2553
2660
|
},
|
|
2554
2661
|
// -----------------------------------------------------------------------
|
|
@@ -2574,7 +2681,7 @@ var OPENAPI_SPEC = {
|
|
|
2574
2681
|
tags: ["NFS-e"],
|
|
2575
2682
|
summary: "Emitir NFS-e",
|
|
2576
2683
|
operationId: "emitNfse",
|
|
2577
|
-
description: "Emite uma NFS-e a partir de uma fatura. O m\xE9todo (certificado/portal) vem da configura\xE7\xE3o da empresa. Pode levar alguns segundos.",
|
|
2684
|
+
description: "Emite uma NFS-e a partir de uma fatura. O m\xE9todo (certificado/portal) vem da configura\xE7\xE3o da empresa. Pode levar alguns segundos. **201** = autorizada ou em transmiss\xE3o; **422** = o fisco REJEITOU (o motivo vem em `message`) \u2014 nesse caso n\xE3o existe nota, n\xE3o afirme ao usu\xE1rio que foi emitida.",
|
|
2578
2685
|
security: [{ ApiKeyAuth: ["nfse:emitir"] }],
|
|
2579
2686
|
requestBody: { required: true, content: { "application/json": { schema: { $ref: "#/components/schemas/NfseEmit" } } } },
|
|
2580
2687
|
responses: { "201": { description: "Emiss\xE3o solicitada", content: { "application/json": { schema: { type: "object", properties: { invoice_id: { type: "string" }, status: { type: "string" }, numero_nfse: { type: ["string", "null"] }, chave_acesso: { type: ["string", "null"] }, history_id: { type: ["string", "null"] } } } } } }, "400": errorResponse("invoice_id ausente"), "404": errorResponse("Fatura n\xE3o encontrada"), "422": errorResponse("NFS-e n\xE3o configurada"), "502": errorResponse("Erro do provedor de NFS-e"), ...COMMON_ERRORS }
|
|
@@ -2621,7 +2728,7 @@ var OPENAPI_SPEC = {
|
|
|
2621
2728
|
tags: ["NFS-e"],
|
|
2622
2729
|
summary: "URLs assinadas do XML e DANFSE",
|
|
2623
2730
|
operationId: "getNfseFiles",
|
|
2624
|
-
description: "Retorna URLs tempor\xE1rias (30 min)
|
|
2731
|
+
description: "Retorna URLs tempor\xE1rias (30 min). `xml_signed_url` = XML autorizado. `danfse_signed_url` = DANFSe em PDF (gerada sob demanda a partir do XML se ainda n\xE3o existir). `danfse_html_signed_url` = HTML OFICIAL publicado pelo ADN, quando dispon\xEDvel \u2014 \xE9 outro documento, n\xE3o uma vers\xE3o pior do PDF. `danfse_formato` diz o que veio em `danfse_signed_url` (pdf|html).",
|
|
2625
2732
|
security: [{ ApiKeyAuth: ["nfse:read"] }],
|
|
2626
2733
|
responses: { "200": { description: "URLs assinadas", content: { "application/json": { schema: { type: "object", properties: { id: { type: "string" }, invoice_id: { type: "string" }, status: { type: "string" }, xml_signed_url: { type: ["string", "null"] }, danfse_signed_url: { type: ["string", "null"] }, signed_url_ttl_seconds: { type: "integer" } } } } } }, "404": errorResponse("NFS-e n\xE3o encontrada"), ...COMMON_ERRORS }
|
|
2627
2734
|
}
|
|
@@ -3365,6 +3472,7 @@ var API_MODULES = [
|
|
|
3365
3472
|
{ key: "create", label: "Criar cobran\xE7a", description: "Gerar boletos, Pix e cobran\xE7as" },
|
|
3366
3473
|
{ key: "cancel", label: "Cancelar cobran\xE7a", description: "Cancelar cobran\xE7as em aberto" },
|
|
3367
3474
|
{ key: "send", label: "Enviar ao cliente", description: "Enviar segunda via da fatura por e-mail ao cliente" },
|
|
3475
|
+
{ key: "settle", label: "Dar baixa", description: "Registrar o recebimento de uma parcela (baixa total ou parcial)" },
|
|
3368
3476
|
{ key: "delete", label: "Excluir fatura", description: "Excluir faturas permanentemente (itens e cobran\xE7as vinculadas)" }
|
|
3369
3477
|
]
|
|
3370
3478
|
},
|
|
@@ -3759,6 +3867,27 @@ function buildCuratedTools(api, heldScopes) {
|
|
|
3759
3867
|
|
|
3760
3868
|
// src/tools/request.ts
|
|
3761
3869
|
var METHODS = ["GET", "POST", "PATCH", "PUT", "DELETE"];
|
|
3870
|
+
var ROTAS_DESTRUTIVAS = (() => {
|
|
3871
|
+
const out = [];
|
|
3872
|
+
for (const entry of MANIFEST) {
|
|
3873
|
+
if (!entry.destructive) continue;
|
|
3874
|
+
let desc;
|
|
3875
|
+
try {
|
|
3876
|
+
desc = describeOperation(entry.operationId);
|
|
3877
|
+
} catch {
|
|
3878
|
+
continue;
|
|
3879
|
+
}
|
|
3880
|
+
const padrao = desc.pathTemplate.split("/").map(
|
|
3881
|
+
(seg) => seg.startsWith("{") && seg.endsWith("}") ? "[^/]+" : seg.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
3882
|
+
).join("/");
|
|
3883
|
+
out.push({ method: desc.method.toUpperCase(), re: new RegExp(`^${padrao}/?$`) });
|
|
3884
|
+
}
|
|
3885
|
+
return out;
|
|
3886
|
+
})();
|
|
3887
|
+
function ehRotaDestrutiva(method, path) {
|
|
3888
|
+
const semQuery = path.split("?")[0];
|
|
3889
|
+
return ROTAS_DESTRUTIVAS.some((r) => r.method === method.toUpperCase() && r.re.test(semQuery));
|
|
3890
|
+
}
|
|
3762
3891
|
function buildRequestTool(api) {
|
|
3763
3892
|
return {
|
|
3764
3893
|
name: "comando_request",
|
|
@@ -3782,7 +3911,7 @@ function buildRequestTool(api) {
|
|
|
3782
3911
|
company_id: { type: "string", description: "Opcional: UUID da empresa (header X-Company-Id)." },
|
|
3783
3912
|
idempotency_key: { type: "string", description: "Opcional: chave de idempot\xEAncia (POST)." },
|
|
3784
3913
|
dry_run: { type: "boolean", description: "Se true, n\xE3o executa \u2014 s\xF3 mostra o que seria enviado." },
|
|
3785
|
-
confirm: { type: "boolean", description: "Obrigat\xF3rio para DELETE
|
|
3914
|
+
confirm: { type: "boolean", description: "Obrigat\xF3rio para DELETE e para qualquer rota destrutiva (cancelar, estornar, pagar, reverter)." }
|
|
3786
3915
|
}
|
|
3787
3916
|
},
|
|
3788
3917
|
handler: async (args) => {
|
|
@@ -3791,13 +3920,13 @@ function buildRequestTool(api) {
|
|
|
3791
3920
|
if (!METHODS.includes(method)) {
|
|
3792
3921
|
return fail(`M\xE9todo inv\xE1lido: ${args.method}. Use um de: ${METHODS.join(", ")}.`);
|
|
3793
3922
|
}
|
|
3794
|
-
|
|
3923
|
+
let path = String(args.path ?? "");
|
|
3924
|
+
if (!path.startsWith("/")) return fail("path deve come\xE7ar com / (ex.: /customers).");
|
|
3925
|
+
if ((method === "DELETE" || ehRotaDestrutiva(method, path)) && args.confirm !== true && args.dry_run !== true) {
|
|
3795
3926
|
return fail(
|
|
3796
3927
|
"A\xE7\xE3o destrutiva bloqueada. Passe confirm: true para executar, ou dry_run: true para pr\xE9-visualizar."
|
|
3797
3928
|
);
|
|
3798
3929
|
}
|
|
3799
|
-
let path = String(args.path ?? "");
|
|
3800
|
-
if (!path.startsWith("/")) return fail("path deve come\xE7ar com / (ex.: /customers).");
|
|
3801
3930
|
const queryArg = coerceObjectArg(args.query);
|
|
3802
3931
|
if (queryArg) {
|
|
3803
3932
|
const u = new URLSearchParams();
|
package/package.json
CHANGED