@burn0/burn0 0.2.2 → 0.2.4

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 CHANGED
@@ -128,6 +128,55 @@ resend $0.15 ░░░░░░░░░░░░░░░░░░░
128
128
 
129
129
  ---
130
130
 
131
+ ## Dashboard — See Everything in One Place
132
+
133
+ The terminal is great for development. But when you want history, trends, and team visibility — connect to the **free dashboard** at [burn0.dev](https://burn0.dev).
134
+
135
+ ### Get started in 60 seconds:
136
+
137
+ ```bash
138
+ # 1. Sign in with GitHub
139
+ open https://burn0.dev/login
140
+
141
+ # 2. Go to your dashboard and copy your API key
142
+ # Dashboard → API Keys → Create Key
143
+
144
+ # 3. Add the key to your project
145
+ echo 'BURN0_API_KEY=b0_sk_your_key_here' >> .env
146
+
147
+ # 4. Restart your app — costs now sync to the dashboard
148
+ npm run dev
149
+ ```
150
+
151
+ That's it. Every API call is now tracked in the dashboard.
152
+
153
+ ### What you'll see:
154
+
155
+ | Feature | Description |
156
+ |---|---|
157
+ | **Live Event Feed** | Every API call streams in real-time — service, model, tokens, cost, latency |
158
+ | **Cost Breakdown** | Per-service, per-model cost breakdown with visual charts |
159
+ | **Monthly Projection** | Estimated monthly spend based on your actual usage trends |
160
+ | **Request History** | Full history of every tracked API call, searchable and filterable |
161
+ | **API Key Management** | Create, list, and revoke keys from the dashboard |
162
+
163
+ ### How it works:
164
+
165
+ ```
166
+ Your app (with BURN0_API_KEY set)
167
+
168
+ ├─ Costs still appear in your terminal (nothing changes)
169
+
170
+ └─ Events also sync to burn0.dev ──→ Dashboard
171
+
172
+ ├─ Only metadata: service, model, tokens, cost, status, latency
173
+ └─ Never request/response bodies. Never your API keys.
174
+ ```
175
+
176
+ > **Privacy**: burn0 never reads your request or response content. Only metadata is synced — service name, model, token counts, cost, status code, and latency. Your API keys and data stay on your machine.
177
+
178
+ ---
179
+
131
180
  ## Feature Attribution
132
181
 
133
182
  Know exactly which feature burns money:
@@ -243,22 +292,9 @@ Your app starts
243
292
  | Mode | API Key | What happens |
244
293
  |---|---|---|
245
294
  | **Local** (default) | No | Costs in terminal + local ledger. Zero network calls to burn0. |
246
- | **Cloud** (opt-in) | Yes | Same as local + events sync to dashboard for team visibility. |
247
-
248
- ### Cloud Dashboard
249
-
250
- Connect an API key to see costs in the browser:
251
-
252
- - **Live event feed** — every API call in real-time via SSE
253
- - **Cost breakdown** — per service, per model, per day
254
- - **Monthly projection** — estimated monthly spend based on trends
255
- - **API key management** — create, list, revoke keys
295
+ | **Cloud** (opt-in) | Yes | Same as local + events sync to [dashboard](https://burn0.dev/dashboard) for full history and team visibility. |
256
296
 
257
- ```bash
258
- # Sign in with GitHub at burn0.dev
259
- # Create an API key, then:
260
- npx burn0 connect
261
- ```
297
+ > **Start local, upgrade when ready.** burn0 works perfectly without an API key. When you want history and dashboards, add a key — it takes 60 seconds. [Get your free API key →](https://burn0.dev/login)
262
298
 
263
299
  ---
264
300
 
@@ -560,7 +560,7 @@ var FREE_SERVICES = /* @__PURE__ */ new Set([
560
560
  "slack-api",
561
561
  "discord-api"
562
562
  ]);
563
- async function fetchPricing(apiUrl, fetchFn) {
563
+ function loadCachedPricing() {
564
564
  try {
565
565
  const cachePath = path2.join(process.cwd(), CACHE_FILE);
566
566
  if (fs2.existsSync(cachePath)) {
@@ -568,11 +568,15 @@ async function fetchPricing(apiUrl, fetchFn) {
568
568
  const cached = JSON.parse(raw);
569
569
  if (Date.now() - cached.cached_at < CACHE_TTL_MS) {
570
570
  pricingData = cached;
571
- return;
571
+ return true;
572
572
  }
573
573
  }
574
574
  } catch {
575
575
  }
576
+ return false;
577
+ }
578
+ async function fetchPricing(apiUrl, fetchFn) {
579
+ if (pricingData) return;
576
580
  try {
577
581
  const response = await fetchFn(`${apiUrl}/v1/pricing`, {
578
582
  headers: { "Accept": "application/json" }
@@ -760,6 +764,7 @@ var apiKey = getApiKey();
760
764
  var mode = detectMode({ isTTY: isTTY(), apiKey });
761
765
  var { track, startSpan, enrichEvent } = createTracker();
762
766
  var originalFetch2 = globalThis.fetch;
767
+ loadCachedPricing();
763
768
  if (mode !== "test-disabled" && mode !== "prod-local") {
764
769
  fetchPricing(BURN0_API_URL, originalFetch2).catch(() => {
765
770
  });
@@ -841,4 +846,4 @@ export {
841
846
  startSpan,
842
847
  restore
843
848
  };
844
- //# sourceMappingURL=chunk-QJYKRVCP.mjs.map
849
+ //# sourceMappingURL=chunk-H3A5NM5C.mjs.map
package/dist/cli/index.js CHANGED
@@ -18380,18 +18380,7 @@ var init_local = __esm({
18380
18380
 
18381
18381
  // src/transport/local-pricing.ts
18382
18382
  async function fetchPricing(apiUrl, fetchFn) {
18383
- try {
18384
- const cachePath = import_node_path8.default.join(process.cwd(), CACHE_FILE);
18385
- if (import_node_fs7.default.existsSync(cachePath)) {
18386
- const raw = import_node_fs7.default.readFileSync(cachePath, "utf-8");
18387
- const cached = JSON.parse(raw);
18388
- if (Date.now() - cached.cached_at < CACHE_TTL_MS) {
18389
- pricingData = cached;
18390
- return;
18391
- }
18392
- }
18393
- } catch {
18394
- }
18383
+ if (pricingData) return;
18395
18384
  try {
18396
18385
  const response = await fetchFn(`${apiUrl}/v1/pricing`, {
18397
18386
  headers: { "Accept": "application/json" }
package/dist/index.js CHANGED
@@ -621,7 +621,7 @@ var FREE_SERVICES = /* @__PURE__ */ new Set([
621
621
  "slack-api",
622
622
  "discord-api"
623
623
  ]);
624
- async function fetchPricing(apiUrl, fetchFn) {
624
+ function loadCachedPricing() {
625
625
  try {
626
626
  const cachePath = import_node_path2.default.join(process.cwd(), CACHE_FILE);
627
627
  if (import_node_fs2.default.existsSync(cachePath)) {
@@ -629,11 +629,15 @@ async function fetchPricing(apiUrl, fetchFn) {
629
629
  const cached = JSON.parse(raw);
630
630
  if (Date.now() - cached.cached_at < CACHE_TTL_MS) {
631
631
  pricingData = cached;
632
- return;
632
+ return true;
633
633
  }
634
634
  }
635
635
  } catch {
636
636
  }
637
+ return false;
638
+ }
639
+ async function fetchPricing(apiUrl, fetchFn) {
640
+ if (pricingData) return;
637
641
  try {
638
642
  const response = await fetchFn(`${apiUrl}/v1/pricing`, {
639
643
  headers: { "Accept": "application/json" }
@@ -821,6 +825,7 @@ var apiKey = getApiKey();
821
825
  var mode = detectMode({ isTTY: isTTY(), apiKey });
822
826
  var { track, startSpan, enrichEvent } = createTracker();
823
827
  var originalFetch2 = globalThis.fetch;
828
+ loadCachedPricing();
824
829
  if (mode !== "test-disabled" && mode !== "prod-local") {
825
830
  fetchPricing(BURN0_API_URL, originalFetch2).catch(() => {
826
831
  });
package/dist/index.mjs CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  restore,
3
3
  startSpan,
4
4
  track
5
- } from "./chunk-QJYKRVCP.mjs";
5
+ } from "./chunk-H3A5NM5C.mjs";
6
6
  import "./chunk-DJ72YN4C.mjs";
7
7
  export {
8
8
  restore,
package/dist/register.js CHANGED
@@ -607,7 +607,7 @@ var FREE_SERVICES = /* @__PURE__ */ new Set([
607
607
  "slack-api",
608
608
  "discord-api"
609
609
  ]);
610
- async function fetchPricing(apiUrl, fetchFn) {
610
+ function loadCachedPricing() {
611
611
  try {
612
612
  const cachePath = import_node_path2.default.join(process.cwd(), CACHE_FILE);
613
613
  if (import_node_fs2.default.existsSync(cachePath)) {
@@ -615,11 +615,15 @@ async function fetchPricing(apiUrl, fetchFn) {
615
615
  const cached = JSON.parse(raw);
616
616
  if (Date.now() - cached.cached_at < CACHE_TTL_MS) {
617
617
  pricingData = cached;
618
- return;
618
+ return true;
619
619
  }
620
620
  }
621
621
  } catch {
622
622
  }
623
+ return false;
624
+ }
625
+ async function fetchPricing(apiUrl, fetchFn) {
626
+ if (pricingData) return;
623
627
  try {
624
628
  const response = await fetchFn(`${apiUrl}/v1/pricing`, {
625
629
  headers: { "Accept": "application/json" }
@@ -807,6 +811,7 @@ var apiKey = getApiKey();
807
811
  var mode = detectMode({ isTTY: isTTY(), apiKey });
808
812
  var { track, startSpan, enrichEvent } = createTracker();
809
813
  var originalFetch2 = globalThis.fetch;
814
+ loadCachedPricing();
810
815
  if (mode !== "test-disabled" && mode !== "prod-local") {
811
816
  fetchPricing(BURN0_API_URL, originalFetch2).catch(() => {
812
817
  });
package/dist/register.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import "./chunk-QJYKRVCP.mjs";
1
+ import "./chunk-H3A5NM5C.mjs";
2
2
  import "./chunk-DJ72YN4C.mjs";
3
3
  //# sourceMappingURL=register.mjs.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burn0/burn0",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Lightweight cost observability for every API call in your stack",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",