npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

cloudbroker-sdk

v0.7.0

Published

CloudBroker SDK — Token Intelligence & Operational Financial Intelligence

Readme

cloudbroker-sdk

Token Intelligence & Operational Financial Intelligence para Node.js.

Instalação

npm install cloudbroker-sdk

Uso com OpenAI

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

const openai = CloudBrokerAI.wrap(new OpenAI(), {
  apiKey:  "cb-key-xxxx",   // sua chave CloudBroker
  product: "checkout",       // nome do produto
  team:    "mobile",         // nome do squad (opcional)
});

// Todas as chamadas seguem iguais — nada muda
const res = await openai.chat.completions.create({
  model:    "gpt-4o",
  messages: [{ role: "user", content: "Olá!" }],
});

Uso com Anthropic

import Anthropic from "@anthropic-ai/sdk";
import { CloudBrokerAI } from "cloudbroker-sdk";

const anthropic = CloudBrokerAI.wrap(new Anthropic(), {
  apiKey:  "cb-key-xxxx",
  product: "checkout",
  team:    "mobile",
});

const res = await anthropic.messages.create({
  model:      "claude-sonnet-4-6",
  max_tokens: 1024,
  messages:   [{ role: "user", content: "Olá!" }],
});

Uso com Gemini

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",
});

const res = await gemini.models.generateContent({
  model:    "gemini-2.5-flash",
  contents: "Olá!",
});

O cbLabel pode ir no próprio params (a API do Gemini é de argumento único) ou no segundo argumento, como nos outros wrappers. Nos dois casos ele é removido antes de a chamada seguir pro Google:

await gemini.models.generateContent({
  model: "gemini-2.5-flash", contents: "…", cbLabel: "checkout:resumo-pedido",
});

Por que o custo do Gemini não é input × preço + output × preço

A conta do Gemini tem três particularidades. Ignorar qualquer uma delas erra o valor de forma relevante, então o SDK trata as três:

| Particularidade | O que acontece se ignorar | |---|---| | promptTokenCount já inclui os tokens servidos de cache, que custam ~10x menos | superfatura a parcela cacheada (1,65x a mais num prompt 80% cacheado) | | thoughtsTokenCount é separado de candidatesTokenCount e é cobrado como output | subfatura — 33x a menos num caso com 2000 thinking tokens | | 2.5 Pro dobra de preço acima de 200k tokens de prompt; áudio na entrada custa mais que texto | subfatura prompts longos e entrada de áudio |

Limitações declaradas

  • O custo é estimado por tabela de preço local, não lido de uma fatura. O Gemini não expõe API de uso ou custo por chave — só o dashboard do AI Studio e o console do Cloud Billing. Os preços são os do paid tier publicados em ai.google.dev/gemini-api/docs/pricing, conferidos em 2026-08-02, e não foram calibrados contra fatura real.
  • Free tier sai com custo > 0. A tabela é a do paid tier; quem está no free tier vê o valor que pagaria, não o que paga.
  • Modelo fora da tabela não recebe preço de outro modelo. O evento vai com costUSD: null e unpriced: true, preservando os tokens. Variante com sufixo (-preview-…) é precificada pelo modelo base e marcada approxPricing.
  • Armazenamento de contexto em cache não é cobrado aqui. O Google cobra por hora de cache ativo (US$ 1,00/h na maioria dos modelos); isso não aparece no usageMetadata de uma chamada e não é modelado.
  • Batch API não é modelada — o Google cobra metade nesse modo.

Streaming

Chamadas com stream: true também são rastreadas — o SDK observa os chunks conforme você os consome e registra o usage ao final, sem alterar o fluxo:

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

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content || "");
}
// tokens/custo são registrados automaticamente quando o stream termina

Para OpenAI, o SDK injeta stream_options: { include_usage: true } para receber o usage no chunk final. Para Anthropic, o usage vem dos eventos message_start / message_delta. O objeto de stream original é preservado (.tee(), .controller, etc.).

Rastreamento de Deploy

import { CloudBrokerAI } from "cloudbroker-sdk";

// Opção 1: Via wrapper (se você está usando OpenAI/Anthropic)
const openai = CloudBrokerAI.wrap(new OpenAI(), {
  apiKey: "cb-key-xxxx",
  product: "checkout",
});

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,
});

// Opção 2: Direto (sem OpenAI/Anthropic)
CloudBrokerAI.trackDeploy("cb-key-xxxx", {
  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,
});

Cost per Feature — Rastreie Custo por Feature

Atribua custo de AWS e Azure a cada feature/módulo sua. O SDK envolve um client do provedor e, a cada chamada, estima o custo (requests + transferência) e registra um evento — mesmo padrão fire-and-forget do wrapper de IA.

👉 Guia Completo — Setup (5 min), exemplos, FAQ.

Escopo atual — AWS: S3 e DynamoDB (us-east-2); Azure: Blob Storage e Cosmos DB (eastus2). Armazenamento GB-mês e atribuição automática (stack-trace) estão fora do MVP.

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",
});

// label opcional do trecho, no 2º argumento do .send()
await s3.send(new PutObjectCommand({ Bucket: "b", Key: "nf.pdf", Body: buf }), {
  cbLabel: "upload-nota-fiscal",
});

Funciona igual com DynamoDB:

import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";

const ddb = CloudBrokerCloud.wrapAWS(new DynamoDBClient({ region: "us-east-2" }), {
  apiKey: "cb-key-xxxx", product: "checkout",
});
await ddb.send(new PutItemCommand({ ... }), { cbLabel: "gravar-pedido" });

Cada chamada gera um evento POST /api/cloud/events com service, operation, product, team, label, requests, bytesIn/bytesOut, costUSD estimado e latencyMs. O cbLabel nunca é repassado ao AWS SDK.

Azure — Blob Storage e Cosmos DB

O Azure SDK não tem um .send() único como o AWS SDK v3: cada sub-client tem métodos próprios. Por isso o wrapAzure intercepta a cadeia de sub-clients — as fábricas (getContainerClient, database/container) devolvem sub-clients já embrulhados, e só os métodos de data-plane conhecidos são rastreados. Qualquer método fora dessa lista passa intocado.

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

const blobSvc = CloudBrokerCloud.wrapAzure(
  BlobServiceClient.fromConnectionString(process.env.AZURE_STORAGE_CONNECTION),
  { apiKey: "cb-key-xxxx", product: "checkout", team: "mobile" },
);

// label opcional dentro do objeto de options nativo do método
await blobSvc
  .getContainerClient("docs")
  .getBlockBlobClient("nf.pdf")
  .uploadData(buf, { cbLabel: "upload-nota-fiscal" });

Cosmos DB usa o requestCharge (RUs) real que o servidor devolve em cada resposta — custo por unidade cobrada de verdade, não aproximação por request:

import { CosmosClient } from "@azure/cosmos";

const cosmos = CloudBrokerCloud.wrapAzure(new CosmosClient(conn), {
  apiKey: "cb-key-xxxx", product: "checkout",
});
const orders = cosmos.database("loja").container("pedidos");

await orders.items.create({ id: "123" }, { cbLabel: "criar-pedido" });
await orders.items.query("SELECT * FROM c").fetchAll(); // RUs do query também contam

O expressMiddleware (abaixo) funciona igual pros dois provedores — mesma prioridade de label/product/team, sem precisar de cbLabel manual.

Precisão e premissas — leia antes de tratar o número como fechado:

  • Cosmos: as RUs são as reais devolvidas pelo servidor em cada resposta (requestCharge). O preço por RU assume o modelo serverless. Em throughput provisionado (manual ou autoscale) a Azure cobra RU/s reservada por hora, independentemente do consumo — esse modelo ainda não é representado, então o custo calculado não corresponde à fatura nesse caso.
  • Blob: estimado por classe de operação (write/read/list), assumindo 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. A Azure não cobra saída intra-região e concede franquia mensal — para um app rodando na mesma região do Storage, este termo superestima.
  • Região: só eastus2 tem tabela de preço. Em outra região o evento é registrado com costUSD: null e billingUnit: "*-region-unpriced" — a operação aparece no relatório, mas sem custo inventado.
  • Reconciliação: a tela compara o estimado contra o custo real do Cost Management e mostra o desvio; o ajuste da tabela é manual (não há recalibração automática).
  • Listagem: listBlobsFlat/listContainers devolvem iterator preguiçoso — o evento só é emitido quando uma página é de fato buscada. Percorrendo com for await, contamos 1 operação mesmo que o SDK pagine internamente; use .byPage() para contagem por página.
  • Cosmos for await direto (getAsyncIterator) não é instrumentado — use fetchAll()/fetchNext().
  • Delete no tier Hot é grátis: sai com custo 0 e billingUnit: "blob-free", sem sumir do relatório.

⚠️ Ainda não calibrado contra uma fatura Azure real. A estrutura é exercitada contra os SDKs oficiais (@azure/storage-blob, @azure/cosmos) na suíte de testes, mas os valores da tabela de preço Azure não foram conferidos contra a página oficial nem contra uma fatura. Trate o custo Azure como ordem de grandeza até rodar a reconciliação na sua conta.

Atribuição automática por requisição (Express)

Em vez de passar cbLabel em cada .send(), use o middleware: todas as chamadas de nuvem feitas durante a requisição herdam um label de negócio automaticamente (via AsyncLocalStorage). Ótimo pra agrupar custo por módulo ou rota.

Cada campo (label, product, team) pode ser fixo (string) ou uma função (req) => string. Isso permite a hierarquia Grupo (team) → App (product) → Trecho (label) → Serviço na visão de custo.

import express from "express";
import { CloudBrokerCloud } from "cloudbroker-sdk";

const app = express();

app.use(CloudBrokerCloud.expressMiddleware({
  team:    (req) => (req.path.startsWith("/api/admin") ? "Plataforma" : "Produto"), // Grupo
  product: (req) => req.path.split("/")[2] || "core",                              // App
  label:   (req) => `${req.method} ${req.path}`,                                   // Trecho
}));

// daqui pra frente, qualquer s3.send(...)/ddb.send(...) dentro da request
// é atribuído ao grupo/app/trecho — sem cbLabel manual.

Prioridade de cada campo: explícito no .send() (cbLabel/cbProduct/cbTeam) > contexto do middleware > valor estático do wrapAWS > null.

Garantias

  • Fire-and-forget: se o CloudBroker estiver fora, sua aplicação não é afetada
  • Zero latência adicional: o tracking acontece em background
  • Retry automático: eventos com falha são reenviados em 30s
  • Fila local: até 500 eventos em memória se o servidor estiver indisponível

API Key CloudBroker

Gere sua chave em app.cloudbroker.app.br → Configurações → API Keys.