Pular para o conteúdo principal

SDK

cloudbroker-sdk é o SDK oficial (Node.js, publicado no npm sem scope — conta npm free não permite pacote scoped). Cobre dois casos de uso: Token Intelligence (custo de IA) e Cost per Feature (custo de nuvem por trecho de código).

npm install cloudbroker-sdk

Gere uma API Key do tipo SDK em app.cloudbroker.app.br → Configurações → aba Conta → CloudBroker SDK (só admin cria; o valor completo aparece uma única vez — ver Administração). Uma key SDK só envia eventos de custo; ela é recusada nas rotas de leitura.

Token Intelligence — custo de IA​

Envolve seu client OpenAI (openai), Anthropic (@anthropic-ai/sdk) ou Gemini (@google/genai) — o provedor é detectado pelo client que você passa. Todas as chamadas continuam funcionando exatamente igual, o SDK só observa e registra o usage em background.

import OpenAI from "openai";
import { CloudBrokerAI } from "cloudbroker-sdk";

const openai = CloudBrokerAI.wrap(new OpenAI(), {
apiKey: "cb-key-xxxx",
product: "checkout",
team: "mobile", // opcional
});

const res = await openai.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: "Olá!" }],
});

Funciona igual com Anthropic (@anthropic-ai/sdk), e com stream: true — o SDK observa os chunks conforme você consome e registra o usage ao final, sem alterar o fluxo original.

Para somar o custo de IA ao trecho de código na visão Custo por Código, passe cbLabel no segundo argumento (create(params, { cbLabel: "checkout:resumo-pedido" })) ou use o middleware Express. O cbLabel nunca é repassado ao provedor.

Modelo fora da tabela de preço

O custo de OpenAI e Anthropic é estimado por tabela de preço local.

  • Anthropic: modelo fora da tabela sai com costUSD: null e unpriced: true, com os tokens preservados — "custo não estimado", não "custo zero". Snapshot com sufixo de data não listado usa o preço do modelo base e é marcado como aproximado (approxPricing).
  • OpenAI: modelo fora da tabela ainda recebe o preço do gpt-4o — trate esse valor como aproximação.

Cache de prompt (Anthropic): os tokens de cache entram no custo com o multiplicador da Anthropic — escrita de 5 min a 1,25×, escrita de 1 h a 2×, leitura a 0,1× o preço de entrada — e vão separados no evento (cacheWriteTokens, cacheReadTokens).

Gemini (@google/genai)​

import { GoogleGenAI } from "@google/genai";
import { CloudBrokerAI } from "cloudbroker-sdk";

const gemini = CloudBrokerAI.wrap(new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY }), {
apiKey: "cb-key-xxxx", product: "checkout", team: "mobile",
});

await gemini.models.generateContent({
model: "gemini-2.5-flash", contents: "Olá!",
cbLabel: "checkout:resumo-pedido", // opcional — removido antes de ir ao Google
});

generateContent e generateContentStream são instrumentados. O custo do Gemini não é input × preço + output × preço — o SDK trata as particularidades do usageMetadata:

  • Cache: promptTokenCount já inclui os tokens servidos de cache (cachedContentTokenCount), que têm preço próprio, bem menor. Só o restante é cobrado a preço cheio de input.
  • Thinking: thoughtsTokenCount vem separado da resposta e é cobrado como output.
  • Faixas: o 2.5 Pro muda de preço acima de 200k tokens de prompt; áudio na entrada tem preço próprio nos modelos que o diferenciam.
Limitações do Gemini
  • O custo é estimado pela tabela do paid tier publicada pelo Google — não foi calibrado contra fatura real. Quem está no free tier vê o valor que pagaria.
  • Modelo fora da tabela não recebe preço de outro modelo: o evento sai com costUSD: null e unpriced: true, preservando os tokens. Isso significa "custo não estimado", não "custo zero". Variantes com sufixo (ex.: -preview-…) usam o preço do modelo base e são marcadas como aproximadas (approxPricing).

Análise de custo de PR (trackDeploy)​

Apesar do nome, trackDeploy não registra um deploy: ele envia os dados da PR para POST /api/analyze/pr, que faz a análise de custo da mudança e, para provider: "github" (o padrão), publica o evento deployment.created (uma PR analisada, que pode ser mergeada dias depois ou nunca). Fire-and-forget, como o resto do SDK.

openai.trackDeploy({
repo: "myapp", branch: "main", prTitle: "feat: add new feature",
prUrl: "https://github.com/org/myapp/pull/42", author: "john.doe",
prNumber: 42, filesChanged: 5, linesAdded: 120, linesRemoved: 30,
});

Para registrar um deploy real (evento deployment.completed), use o passo de CI que chama POST /api/deploys/completed — veja Registrando deploys.

Cost per Feature — custo de nuvem por linha de código​

Envolve um client do provedor de nuvem e, a cada chamada, estima o custo (requests + transferência) e registra um evento.

Escopo atual
  • AWS (wrapAWS): S3 e DynamoDB, região us-east-2.
  • Azure (wrapAzure): Blob Storage e Cosmos DB, região eastus2 — ver Azure.

Armazenamento GB-mês e atribuição automática por stack-trace estão fora do escopo atual.

import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { CloudBrokerCloud } from "cloudbroker-sdk";

const s3 = CloudBrokerCloud.wrapAWS(new S3Client({ region: "us-east-2" }), {
apiKey: "cb-key-xxxx", product: "checkout", team: "mobile",
});

await s3.send(new PutObjectCommand({ Bucket: "b", Key: "nf.pdf", Body: buf }), {
cbLabel: "upload-nota-fiscal",
});

Cada chamada gera um evento com service, operation, product, team, label, requests, bytesIn/bytesOut, costUSD estimado e latencyMs.

Atribuição automática via middleware Express​

Em vez de passar cbLabel em cada .send(), use o middleware — toda chamada de nuvem feita durante a requisição herda um label automaticamente via AsyncLocalStorage:

import { CloudBrokerCloud } from "cloudbroker-sdk";

app.use(CloudBrokerCloud.expressMiddleware({
team: (req) => (req.path.startsWith("/api/admin") ? "Plataforma" : "Produto"),
product: (req) => req.path.split("/")[2] || "core",
// label omitido = padrão: "MÉTODO /caminho" com ids trocados por ":id"
}));

Prioridade: cbLabel/cbProduct/cbTeam explícito no .send() > contexto do middleware > valor estático do wrapAWS > null. O middleware vale igual para wrapAzure e para o wrapper de IA.

Rótulo padrão do middleware (0.9.3)

Sem label, o trecho é MÉTODO /caminho com os segmentos que parecem identificador trocados por :id — número, UUID, hex longo, e-mail, token longo com dígito e data. GET /pedidos/123 vira GET /pedidos/:id e GET /clientes/maria@empresa.com vira GET /clientes/:id; no máximo 6 segmentos e 120 caracteres.

Até a 0.9.2 o padrão era o caminho cru: o middleware roda no app.use, antes de o Express casar a rota, então cada id virava um trecho diferente e ia gravado no rótulo. Se você usa a 0.9.2 ou anterior sem label, atualize para a 0.9.3.

Para montar o seu próprio rótulo, reaproveite routeLabel(req) ou normalizePath(caminho), exportados pelo pacote. Não use req.path cru.

Custo por execução e versão do código​

Cada evento do wrapAWS/wrapAzure é uma chamada de API, não uma execução do trecho. Só com chamadas não dá para saber se o código passou a chamar mais ou se veio mais tráfego. O ExecutionCounter (versão 0.8.0 ou mais nova) conta as execuções e fecha essa conta: custo por execução e chamadas por execução.

import { CloudBrokerCloud, ExecutionCounter, normalizePath } from "cloudbroker-sdk";

const execucoes = new ExecutionCounter({ apiKey: process.env.CLOUDBROKER_API_KEY });

app.use(CloudBrokerCloud.expressMiddleware({
product: "checkout",
label: (req) => normalizePath(req.path).split("/").slice(0, 3).join("/"), // prefixo da rota, ids viram ":id"
executions: execucoes, // 1 execução por requisição
}));

// Fora do Express (jobs, filas, workers):
await CloudBrokerCloud.trackExecution("job:fechamento-mensal", () => fecharMes(), {
product: "financeiro", executions: execucoes,
});
  • A contagem fica em memória, agregada por trecho, app, versão e hora, e sai em lote a cada 60 s para POST /api/cloud/executions. Nada de rede no caminho da requisição.
  • Rótulo de baixa cardinalidade: acima de 200 combinações pendentes, o excedente é descartado e somado em execucoes.dropped.
  • Versão do código: defina CLOUDBROKER_VERSION (ex.: o SHA do build) ou passe version ao ExecutionCounter e ao wrapAWS/wrapAzure. Ela vai nas execuções e nas chamadas, para comparar o trecho antes e depois de um deploy pela versão que de fato rodou.
  • Falhas e duração (0.9.1): sem mudar o código, o middleware e o trackExecution informam quantas execuções terminaram, quantas falharam (requisição com status 5xx; callback que lançou ou Promise rejeitada) e a duração somada. Requisição abortada antes de responder conta como execução, mas não como terminada — por isso taxa de falha e duração média são calculadas só sobre as terminadas. Versões anteriores do SDK não informam esses campos, e eles aparecem como "não medido", nunca como "0 falhas".
  • No shutdown: await execucoes.flush(); execucoes.stop();.

O FinOps Advisor usa essa contagem para responder se o custo de um trecho mudou com os deploys. Comparação por deploy só sai com pelo menos 24 h e 30 execuções de cada lado; abaixo disso a resposta diz "volume insuficiente", sem percentual.

Azure — Blob Storage e Cosmos DB​

O Azure SDK não tem um .send() único: o wrapAzure intercepta a cadeia de sub-clients (getContainerClient → getBlockBlobClient, database → container → items) e rastreia só os métodos de data-plane conhecidos. O cbLabel vai no objeto de options nativo do método.

import { BlobServiceClient } from "@azure/storage-blob";
import { CosmosClient } from "@azure/cosmos";
import { CloudBrokerCloud } from "cloudbroker-sdk";

const blobSvc = CloudBrokerCloud.wrapAzure(
BlobServiceClient.fromConnectionString(process.env.AZURE_STORAGE_CONNECTION),
{ apiKey: "cb-key-xxxx", product: "checkout", team: "mobile" },
);
await blobSvc.getContainerClient("docs").getBlockBlobClient("nf.pdf")
.uploadData(buf, { cbLabel: "upload-nota-fiscal" });

const cosmos = CloudBrokerCloud.wrapAzure(new CosmosClient(conn), {
apiKey: "cb-key-xxxx", product: "checkout",
});
await cosmos.database("loja").container("pedidos").items
.create({ id: "123" }, { cbLabel: "criar-pedido" });

No Cosmos DB, as RUs usadas são as reais devolvidas pelo servidor (requestCharge); o que é estimado é o preço por RU.

Premissas do custo Azure — leia antes de tratar o número como fechado
  • Cosmos: preço por RU assume o modelo serverless. Em throughput provisionado (manual ou autoscale) a Azure cobra RU/s reservada — esse modelo não é representado, e o custo calculado não corresponde à fatura.
  • Blob: assume tier Hot e redundância LRS. Cool/Archive e GRS/ZRS têm outro preço.
  • Transferência de saída: cobrada sobre todo bytesOut; para app na mesma região do Storage, superestima.
  • Região: só eastus2 tem tabela de preço. Em outra região o evento é registrado com costUSD: null (billingUnit: "*-region-unpriced") — aparece no relatório, sem custo inventado.
  • Não calibrado contra fatura Azure real. Trate como ordem de grandeza até comparar com a reconciliação na sua conta.

Reconciliação com a fatura real​

O custo estimado pelo SDK é comparado automaticamente contra o custo real do provedor (Cost Explorer na AWS, Cost Management no Azure) — a cobertura mede quanto do custo real os trechos instrumentados explicam (estimado sempre ≤ real, nunca o inverso).

Garantias​

  • Fire-and-forget: se o CloudBroker estiver fora do ar, sua aplicação não é afetada
  • Zero latência adicional: tracking acontece em background
  • Retry automático: eventos com falha são reenviados em 30s
  • Reenvio não duplica custo (0.9.2): cada evento recebe um eventId (ev_ + UUID v7) ao entrar na fila, e o retry reenvia o mesmo id. O CloudBroker reconhece o evento já gravado e não o conta de novo
  • Fila local: até 500 eventos em memória se o servidor estiver indisponível. Acima disso o evento é descartado e contado: client.stats() devolve aiDropped/cloudDropped, e a contagem também vai ao CloudBroker no próximo lote
  • Versão do preço (0.9.2): cada evento leva pricingVersion, a versão da tabela de preço que estimou o custo

MCP​

O cloudbroker-mcp é um servidor MCP (Model Context Protocol) que conecta seu assistente de IA — Claude Desktop, Cursor, ChatGPT — direto aos seus dados do CloudBroker, sem abrir a console.

{
"mcpServers": {
"cloudbroker": {
"command": "npx",
"args": ["-y", "cloudbroker-mcp"],
"env": { "CLOUDBROKER_API_KEY": "cb_live_xxx" }
}
}
}

São 23 tools (versão 0.10.0).

Leitura (17): get_cost_overview, list_apps, list_groups, get_forecast, get_ai_usage, get_recommendations, get_anomalies, analyze_cost_increase, compare_periods, get_deploys, list_deploys, get_top_services, get_saas_costs, get_daily_costs, list_findings, get_shadow_report, discovery_scan.

list_deploys lista os deploys que de fato aconteceram (registrados pelo passo de CI) e o que cada um mudou — ver Registrando deploys. get_deploys lista análises de PR com custo estimado, não deploys.

Escrita (6) — o cliente MCP pede aprovação a cada chamada, e o backend exige papel admin: set_budget, create_app, update_app, create_group, update_group, review_finding.

A autenticação é via API Key do tipo MCP (Configurações → aba Conta → API Key Pessoal (MCP)), enviada no header x-api-key — mesmos endpoints REST usados pela interface. Qualquer papel pode criar a sua; o que ela consegue fazer segue o papel atual de quem a criou (um viewer usa as tools de leitura, e as de escrita respondem 403). Uma key MCP é recusada nas rotas de ingestão do SDK. Detalhes em Administração.