openzoo 0.51.9 → 0.51.11

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.
@@ -1,10 +1,9 @@
1
1
  const usd = n => typeof n === 'number' && Number.isFinite(n) ? '$' + Math.abs(n).toFixed(Math.abs(n) < .01 && n !== 0 ? 4 : 2) : '—';
2
2
  window.savings.listen(d => {
3
- for (const [id, value] of [['wallet',d.walletUsd],['credit',d.creditUsd],['spent',d.spendUsd],['direct',d.directUsd]]) document.getElementById(id).textContent=usd(value);
4
- const comparable=typeof d.directUsd==='number' && typeof d.spendUsd==='number' && (d.directUsd>0 || d.spendUsd===0);
5
- const saved=comparable ? d.directUsd-d.spendUsd : null;
3
+ for (const [id, value] of [['wallet',d.walletUsd],['credit',d.creditUsd],['spent',d.billing?.calls > 0 ? d.billing.chargedUsd : null],['direct',d.comparison?.calls > 0 ? d.comparison.directUsd : null]]) document.getElementById(id).textContent=usd(value);
4
+ const saved=d.comparison?.calls > 0 ? d.comparison.directUsd-d.comparison.spentUsd : null;
6
5
  document.getElementById('saved').textContent=usd(saved);
7
6
  document.getElementById('saving-label').textContent=saved!==null && saved<0 ? 'Extra cost' : 'Saved';
8
- document.getElementById('state').textContent=d.online ? 'Balance updates automatically' : 'Reconnecting · showing last known amounts';
7
+ document.getElementById('state').textContent=d.online ? (d.billing?.calls > 0 ? 'Final charges · saved receipts' : 'Waiting for final billing · balance is live') : 'Reconnecting · showing last known amounts';
9
8
  });
10
9
  document.getElementById('close').addEventListener('click',()=>window.savings.close());
@@ -1,3 +1,3 @@
1
1
  <!doctype html><html><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; script-src 'self'"><title>OpenZoo savings</title><style>
2
2
  *{box-sizing:border-box}body{margin:0;padding:8px;background:transparent;font:13px system-ui;color:#fff}.pill{border-radius:20px;background:#18181bf5;border:1px solid #ffffff25;overflow:hidden;box-shadow:0 4px 14px #0004}header{background:#e85d1c;padding:12px 16px;font-weight:650;-webkit-app-region:drag;display:flex;justify-content:space-between;align-items:center}button{-webkit-app-region:no-drag;border:0;background:none;color:inherit;font:18px system-ui;cursor:pointer;padding:0 5px}main{padding:12px 16px}dl{display:grid;grid-template-columns:1fr auto;margin:0;gap:7px}dd{margin:0;font-variant-numeric:tabular-nums}small{color:#bbb;display:block;margin-top:12px;font-size:11px}#state{color:#d8ff76}
3
- </style></head><body><div class="pill"><header><span>OpenZoo · costs & savings</span><button id="close" aria-label="Close savings pill">×</button></header><main><dl><dt>Wallet balance</dt><dd id="wallet">—</dd><dt>Prepaid credit</dt><dd id="credit">—</dd><dt>Spent</dt><dd id="spent">—</dd><dt>Would’ve cost · estimate</dt><dd id="direct">—</dd><dt id="saving-label">Saved</dt><dd id="saved">—</dd></dl><small id="state" role="status">Checking your balance…</small><small>Shared OpenZoo totals · drag the orange bar to move</small></main></div><script src="savings-renderer.js"></script></body></html>
3
+ </style></head><body><div class="pill"><header><span>OpenZoo · costs & savings</span><button id="close" aria-label="Close savings pill">×</button></header><main><dl><dt>Wallet balance</dt><dd id="wallet">—</dd><dt>Prepaid credit</dt><dd id="credit">—</dd><dt>Charged · verified calls</dt><dd id="spent">—</dd><dt>Direct cost · verified calls</dt><dd id="direct">—</dd><dt id="saving-label">Saved</dt><dd id="saved">—</dd></dl><small id="state" role="status">Checking your balance…</small><small>Savings cover matching verified calls only. Drag the orange bar to move.</small></main></div><script src="savings-renderer.js"></script></body></html>
package/lib/billing.js ADDED
@@ -0,0 +1,33 @@
1
+ const money = n => typeof n === 'number' && Number.isFinite(n) && n >= 0 ? n : null;
2
+
3
+ // Only final, explicitly reported charges. Never substitute upstream cost or a quote.
4
+ export function finalBilling(x = {}, usage = {}) {
5
+ const receipt = x._completedResponse === true || x.settle?.success === true;
6
+ const charged = [usage.billedUsd, x.billedActualUsd, x.settledBilledUsd, x.chargedUsd,
7
+ x.settledUsd, receipt ? x.billedUsd : null,
8
+ usage.cost_details?.upstream_inference_cost != null ? usage.cost : null].map(money).find(n => n !== null) ?? null;
9
+ const direct = receipt ? money(x.directUsd) : null;
10
+ const upstream = money(usage.cost_details?.upstream_inference_cost) ?? (receipt ? money(x.actualUsd) : null);
11
+ return { charged, direct, upstream };
12
+ }
13
+
14
+ // Observe billing without retaining prompts, output text, or changing the stream.
15
+ export function billingObserver(onReceipt) {
16
+ let pending = '';
17
+ return chunk => {
18
+ pending += chunk;
19
+ let i;
20
+ while ((i = pending.indexOf('\n')) >= 0) {
21
+ const line = pending.slice(0, i).trimEnd(); pending = pending.slice(i + 1);
22
+ try {
23
+ if (line.startsWith(': x402 ')) onReceipt(JSON.parse(line.slice(7)));
24
+ else if (line.startsWith('data:')) {
25
+ const event = JSON.parse(line.slice(5).trim());
26
+ const value = event.response || event;
27
+ if (value.usage || value.x402) onReceipt({ ...value.x402, usage: value.usage, _completedResponse: event.type === 'response.completed' || value.status === 'completed' });
28
+ }
29
+ } catch { /* Not a billing event. */ }
30
+ }
31
+ if (pending.length > 2_000_000) pending = '';
32
+ };
33
+ }
package/lib/proxy.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { billingObserver, finalBilling } from './billing.js';
1
2
  import { liveBalance } from './funding-status.js';
2
3
  import { paymentError } from './payment-error.js';
3
4
  import { readFileSync, appendFileSync, mkdirSync } from 'node:fs';
@@ -234,16 +235,10 @@ function relay(res, upstream, onReceipt) {
234
235
  res.on('close', () => body.destroy());
235
236
  body.on('end', resolve);
236
237
  if (!sse || typeof onReceipt !== 'function') { body.pipe(res); return; }
237
- let pending = '';
238
+ const observe = billingObserver(onReceipt);
238
239
  body.on('data', (c) => {
239
240
  res.write(c);
240
- pending += c.toString('utf8');
241
- const lines = pending.split('\n');
242
- pending = lines.pop() ?? ''; // a comment can straddle two chunks
243
- for (const line of lines) {
244
- if (!line.startsWith(': x402 ')) continue;
245
- try { onReceipt(JSON.parse(line.slice(7))); } catch { /* not our frame */ }
246
- }
241
+ observe(c.toString('utf8'));
247
242
  });
248
243
  body.on('end', () => res.end());
249
244
  });
@@ -429,8 +424,10 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
429
424
  let lastQuoteUsd = null;
430
425
  let sessionCogs = restored.cogsUsd;
431
426
  let sessionDirect = restored.directUsd;
427
+ let comparedSpent = restored.comparison?.spentUsd ?? 0, comparedDirect = restored.comparison?.directUsd ?? 0, comparedCalls = restored.comparison?.calls ?? 0;
428
+ let verifiedCharged = restored.billing?.chargedUsd ?? 0, verifiedCalls = restored.billing?.calls ?? 0;
432
429
  const rememberSpend = () => {
433
- saveSessionSpend({ spentUsd: sessionSpent, cogsUsd: sessionCogs, directUsd: sessionDirect, paidCalls });
430
+ saveSessionSpend({ spentUsd: sessionSpent, cogsUsd: sessionCogs, directUsd: sessionDirect, paidCalls, billing: { chargedUsd: verifiedCharged, calls: verifiedCalls }, comparison: { spentUsd: comparedSpent, directUsd: comparedDirect, calls: comparedCalls } });
434
431
  };
435
432
  if (restored.ok && (sessionSpent > 0 || paidCalls > 0)) {
436
433
  say(`session restored: $${sessionSpent.toFixed(6)} · ${paidCalls} paid call${paidCalls === 1 ? '' : 's'}`);
@@ -659,6 +656,8 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
659
656
  creditUsd,
660
657
  // WHAT THE SAME CALLS WOULD HAVE COST DIRECT. Spend on its own is a
661
658
  // bill; spend beside the counterfactual is the product.
659
+ billing: { chargedUsd: verifiedCharged, calls: verifiedCalls, scope: "saved verified receipts" },
660
+ comparison: { spentUsd: comparedSpent, directUsd: comparedDirect, calls: comparedCalls },
662
661
  directUsd: sessionDirect,
663
662
  savedUsd: Math.max(0, sessionDirect - sessionSpent),
664
663
  savingX: sessionSpent > 0 ? Number((sessionDirect / sessionSpent).toFixed(4)) : null,
@@ -965,36 +964,39 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
965
964
  }
966
965
  scheduleRefresh(4000); // settlement lands on-chain in a few seconds
967
966
  }
967
+ let accounted = paid && receipt?.ok && typeof receipt.billedUsd === 'number' ? receipt.billedUsd : 0;
968
+ let counted = paid && receipt?.ok && typeof receipt.billedUsd === 'number';
969
+ let finalX = {}, finalUsage = {}, comparison = null, actualPair = null, verified = null;
970
+ const meterFinal = (x = {}) => {
971
+ const { usage, ...fields } = x;
972
+ finalX = { ...finalX, ...fields };
973
+ finalUsage = { ...finalUsage, ...usage };
974
+ const final = finalBilling(finalX, finalUsage);
975
+ if (final.charged === null) return;
976
+ if (verified === null) verifiedCalls++;
977
+ verifiedCharged += final.charged - (verified ?? 0);
978
+ verified = final.charged;
979
+ const delta = final.charged - accounted;
980
+ sessionSpent += delta;
981
+ if (viaTunnel) tunnelSpent += delta;
982
+ if (!counted) { paidCalls++; counted = true; }
983
+ accounted = final.charged;
984
+ if (comparison) { comparedSpent -= comparison.charged; comparedDirect -= comparison.direct; comparedCalls--; comparison = null; }
985
+ if (final.direct !== null) {
986
+ comparison = final;
987
+ comparedSpent += final.charged; comparedDirect += final.direct; comparedCalls++;
988
+ }
989
+ if (actualPair) { sessionActual -= actualPair.upstream; billedWithActual -= actualPair.charged; actualCalls--; }
990
+ actualPair = final.upstream === null ? null : final;
991
+ if (actualPair) { sessionActual += final.upstream; billedWithActual += final.charged; actualCalls++; }
992
+ rememberSpend();
993
+ log(`final billing: charged $${final.charged.toFixed(6)} · upstream ${final.upstream === null ? 'unreported' : '$' + final.upstream.toFixed(6)} · direct ${final.direct === null ? 'unreported' : '$' + final.direct.toFixed(6)}`);
994
+ };
968
995
  const upCt = response.headers.get('content-type') || '';
969
996
  if (isPaidPost && response.ok && upCt.includes('application/json')) {
970
997
  let data = null;
971
998
  try { data = await response.clone().json(); } catch { /* not JSON after all */ }
972
- // WHAT IT REALLY COST, straight from the provider — rides every
973
- // completion already; no extra call.
974
- {
975
- // PAIR THE NUMERATOR WITH THE DENOMINATOR: usage.cost with the
976
- // post-completion billed twin, never the quote reserve.
977
- const pair = pairActualBilled(data?.x402, data?.usage);
978
- if (pair) {
979
- sessionActual += pair.upstreamUsd;
980
- actualCalls += 1;
981
- billedWithActual += pair.billedUsd;
982
- }
983
- }
984
- // PREPAID CALLS STILL COST MONEY. When prepaid credit covers the quote
985
- // the gateway serves 200 on the FIRST request — no 402, no payment, no
986
- // receipt. The receipt still rides the response body, so meter it there.
987
- if (!paid && data?.x402 && typeof data.x402.billedUsd === 'number') {
988
- const x = data.x402;
989
- sessionSpent += x.billedUsd;
990
- sessionCogs += receiptUsedCogs(x);
991
- noteQuote(x);
992
- sessionDirect += receiptDirectUsd(x);
993
- paidCalls += 1;
994
- if (viaTunnel) tunnelSpent += x.billedUsd;
995
- rememberSpend();
996
- say(`credit -> $${x.billedUsd.toFixed(6)} · session $${sessionSpent.toFixed(6)}`);
997
- }
999
+ meterFinal({ ...data?.x402, usage: data?.usage, _completedResponse: response.ok });
998
1000
  if (data?.object === 'chat.completion') {
999
1001
  if (statsOff) {
1000
1002
  // The gateway already dropped its half; drop ours, and any block an
@@ -1020,23 +1022,7 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
1020
1022
  // A STREAMED call is metered from the gateway's trailing SSE comment —
1021
1023
  // same figures the JSON path reads out of `data.x402`, same counters, so
1022
1024
  // the status line does not care which transport served the answer.
1023
- const meterStreamed = (x) => {
1024
- const pair = pairActualBilled(x, x?.usage);
1025
- if (pair) {
1026
- sessionActual += pair.upstreamUsd;
1027
- actualCalls += 1;
1028
- billedWithActual += pair.billedUsd;
1029
- }
1030
- if (paid || typeof x?.billedUsd !== 'number') return;
1031
- sessionSpent += x.billedUsd;
1032
- sessionCogs += receiptUsedCogs(x);
1033
- noteQuote(x);
1034
- sessionDirect += receiptDirectUsd(x);
1035
- paidCalls += 1;
1036
- if (viaTunnel) tunnelSpent += x.billedUsd;
1037
- rememberSpend();
1038
- say(`credit -> $${x.billedUsd.toFixed(6)} · session $${sessionSpent.toFixed(6)}`);
1039
- };
1025
+ const meterStreamed = meterFinal;
1040
1026
  // A 402 AFTER we attempted payment is a SETTLEMENT failure, not a quote.
1041
1027
  // The client-side balance check is advisory, so a funded wallet can still
1042
1028
  // fail on-chain / at the facilitator, and the gateway answers the paid
package/lib/session.js CHANGED
@@ -30,6 +30,8 @@ export function loadSessionSpend(file = sessionSpendFile()) {
30
30
  try { data = JSON.parse(raw); }
31
31
  catch { return { ...EMPTY, ok: false, reason: 'corrupt' }; }
32
32
  return {
33
+ ...(data.billing ? { billing: { chargedUsd: num(data.billing.chargedUsd), calls: Math.floor(num(data.billing.calls)) } } : {}),
34
+ ...(data.comparison ? { comparison: { spentUsd: num(data.comparison.spentUsd), directUsd: num(data.comparison.directUsd), calls: Math.floor(num(data.comparison.calls)) } } : {}),
33
35
  spentUsd: num(data.spentUsd),
34
36
  cogsUsd: num(data.cogsUsd),
35
37
  directUsd: num(data.directUsd),
@@ -40,6 +42,8 @@ export function loadSessionSpend(file = sessionSpendFile()) {
40
42
 
41
43
  export function saveSessionSpend(stats, file = sessionSpendFile()) {
42
44
  const payload = {
45
+ ...(stats?.billing ? { billing: { chargedUsd: num(stats.billing.chargedUsd), calls: Math.floor(num(stats.billing.calls)) } } : {}),
46
+ ...(stats?.comparison ? { comparison: { spentUsd: num(stats.comparison.spentUsd), directUsd: num(stats.comparison.directUsd), calls: Math.floor(num(stats.comparison.calls)) } } : {}),
43
47
  spentUsd: num(stats?.spentUsd),
44
48
  cogsUsd: num(stats?.cogsUsd),
45
49
  directUsd: num(stats?.directUsd),
package/lib/x402.js CHANGED
@@ -432,49 +432,22 @@ export async function tokenBalance(connection, owner, mintStr) {
432
432
  export function receiptLine(accept, settle) {
433
433
  const x = accept.extra || {};
434
434
  const usd = x.billedUsd != null ? `$${Number(x.billedUsd).toFixed(6)}` : `${accept.maxAmountRequired} raw units`;
435
- // "1.0× cheaper than direct" is a sentence that means nothing, and a user
436
- // read it as a bug — rightly. Since the gateway repriced to an OpenRouter
437
- // CEILING, an uncompressed call bills exactly the direct rate, so the ratio
438
- // is 1.0 by design rather than by accident. Say what actually happened:
439
- // below 1.05× there is no saving to report, so report the price instead.
440
- //
441
- // The saving comes from leCore forwarding fewer tokens. A short body never
442
- // reaches the spill threshold, so there is nothing to compress and nothing
443
- // to save — which is worth saying out loud, because the fix on the caller's
444
- // side is to BIND a corpus, not to change models.
445
435
  const billed = Number(x.billedUsd);
446
436
  const direct = x.directUsd != null ? Number(x.directUsd) : null;
447
437
  const saved = x.savedUsd != null ? Number(x.savedUsd)
448
438
  : (Number.isFinite(billed) && Number.isFinite(direct) ? Math.max(0, direct - billed) : null);
449
439
  const ratio = x.savesVsDirect != null ? Number(x.savesVsDirect)
450
440
  : (Number.isFinite(billed) && billed > 0 && Number.isFinite(direct) ? direct / billed : null);
451
- // Wallet path: OpenRouter price, plus 33% of savings vs direct when any.
452
- // Never print extra.markup — that field is leftover 3× and is not the quote.
453
- //
454
- // THE BASIS DECIDES THE CLAIM, NOT THE RATIO'S SIZE.
455
- //
456
- // This used to gate the discount purely on `ratio >= 1.05`, which is the
457
- // wrong test: on the `markup` basis the gateway sets directUsd to its OWN
458
- // forwarded cost so downstream sums stay like-for-like (quote.ts), and
459
- // savesVsDirect is computed against that same internal number — so the ratio
460
- // can read well above 1 while billedUsd and directUsd are IDENTICAL.
461
- // MEASURED 2026-08-28 on a live short-body call: billed $0.0013178, direct
462
- // $0.0013178, savesVsDirect 1.457 — and this line printed "1.5× cheaper than
463
- // direct" on a call that by its own two dollar figures saved nothing.
464
- //
465
- // The 402's own `description` already guards this correctly by branching on
466
- // q.pricing; the receipt did not, so the challenge told the truth and the
467
- // receipt for the same call did not. Only the counterfactual basis — leCore
468
- // actually spilled, so "what this would have cost sent whole" is a real
469
- // computed number — has earned the discount claim.
441
+ // Only counterfactual pricing supplies a retail comparison. Other modes
442
+ // may put internal costs in directUsd; they do not establish price parity.
470
443
  const counterfactual = x.pricing === 'counterfactual';
471
444
  const saves = !counterfactual
472
- ? ' (at OpenRouter price — nothing to compress; bind a corpus to save)'
445
+ ? ' (direct-price comparison unavailable)'
473
446
  : ratio != null && ratio >= 1.05
474
447
  ? ` (${ratio.toFixed(1)}× cheaper than direct)`
475
448
  : Number.isFinite(saved) && saved > 0
476
449
  ? ` ($${saved.toFixed(4)} saved vs direct)`
477
- : ' (at OpenRouter price — nothing to compress; bind a corpus to save)';
450
+ : ' (direct-price comparison unavailable)';
478
451
  const tx = settle?.transaction || settle?.txHash || settle?.signature;
479
452
  const rail = railOf(accept);
480
453
  return `paid ${usd}${saves}${rail ? ` · rail ${rail}` : ''}${tx ? ` · tx ${tx}` : ''}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.51.9",
3
+ "version": "0.51.11",
4
4
  "description": "Local x402-paying proxy + MCP server for openzoo.fun — point any OpenAI-compatible harness (Cursor, Claude Code, aider, SDKs) at localhost and it pays per call from a local burner wallet. Solana and Base rails live; Robinhood experimental.",
5
5
  "license": "MIT",
6
6
  "type": "module",