openzoo 0.51.11 → 0.51.12

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.
@@ -2,7 +2,8 @@ const usd = n => typeof n === 'number' && Number.isFinite(n) ? '$' + Math.abs(n)
2
2
  window.savings.listen(d => {
3
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
4
  const saved=d.comparison?.calls > 0 ? d.comparison.directUsd-d.comparison.spentUsd : null;
5
- document.getElementById('saved').textContent=usd(saved);
5
+ const pct=d.comparison?.directUsd > 0 && saved !== null ? Math.abs(saved)/d.comparison.directUsd*100 : null;
6
+ document.getElementById('saved').textContent=usd(saved)+(pct===null?'':` (${pct.toFixed(1)}%)`);
6
7
  document.getElementById('saving-label').textContent=saved!==null && saved<0 ? 'Extra cost' : 'Saved';
7
8
  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';
8
9
  });
package/lib/billing.js CHANGED
@@ -24,7 +24,7 @@ export function billingObserver(onReceipt) {
24
24
  else if (line.startsWith('data:')) {
25
25
  const event = JSON.parse(line.slice(5).trim());
26
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' });
27
+ if (value.usage || value.x402) onReceipt({ ...value.x402, usage: value.usage, _responseId: value.id, _completedResponse: event.type === 'response.completed' || value.status === 'completed' });
28
28
  }
29
29
  } catch { /* Not a billing event. */ }
30
30
  }
package/lib/proxy.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { receiptLedger } from './receipt-ledger.js';
1
2
  import { billingObserver, finalBilling } from './billing.js';
2
3
  import { liveBalance } from './funding-status.js';
3
4
  import { paymentError } from './payment-error.js';
@@ -35,7 +36,7 @@ async function catalogIdsCached(modelsUrl, headers) {
35
36
  return ids;
36
37
  }
37
38
  import { withNamespace } from './namespace.js';
38
- import { loadSessionSpend, saveSessionSpend } from './session.js';
39
+ import { loadSessionSpend, saveSessionSpend, sessionSpendFile } from './session.js';
39
40
  import { creditBalance, quotedPrices } from './info.js';
40
41
  import { priceHoldings } from './livestatus.js';
41
42
  import { receiptUsedCogs, receiptDirectUsd, pairActualBilled } from './racesettle.js';
@@ -426,6 +427,12 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
426
427
  let sessionDirect = restored.directUsd;
427
428
  let comparedSpent = restored.comparison?.spentUsd ?? 0, comparedDirect = restored.comparison?.directUsd ?? 0, comparedCalls = restored.comparison?.calls ?? 0;
428
429
  let verifiedCharged = restored.billing?.chargedUsd ?? 0, verifiedCalls = restored.billing?.calls ?? 0;
430
+ const ledger = receiptLedger(sessionSpendFile() + '.receipts.json', { billing: restored.billing, comparison: restored.comparison });
431
+ const applyLedger = ({billing, comparison}) => {
432
+ verifiedCharged = billing.chargedUsd; verifiedCalls = billing.calls;
433
+ comparedSpent = comparison.spentUsd; comparedDirect = comparison.directUsd; comparedCalls = comparison.calls;
434
+ };
435
+ applyLedger(ledger.totals());
429
436
  const rememberSpend = () => {
430
437
  saveSessionSpend({ spentUsd: sessionSpent, cogsUsd: sessionCogs, directUsd: sessionDirect, paidCalls, billing: { chargedUsd: verifiedCharged, calls: verifiedCalls }, comparison: { spentUsd: comparedSpent, directUsd: comparedDirect, calls: comparedCalls } });
431
438
  };
@@ -964,6 +971,7 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
964
971
  }
965
972
  scheduleRefresh(4000); // settlement lands on-chain in a few seconds
966
973
  }
974
+ const requestReceiptId = crypto.randomUUID();
967
975
  let accounted = paid && receipt?.ok && typeof receipt.billedUsd === 'number' ? receipt.billedUsd : 0;
968
976
  let counted = paid && receipt?.ok && typeof receipt.billedUsd === 'number';
969
977
  let finalX = {}, finalUsage = {}, comparison = null, actualPair = null, verified = null;
@@ -989,6 +997,7 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
989
997
  if (actualPair) { sessionActual -= actualPair.upstream; billedWithActual -= actualPair.charged; actualCalls--; }
990
998
  actualPair = final.upstream === null ? null : final;
991
999
  if (actualPair) { sessionActual += final.upstream; billedWithActual += final.charged; actualCalls++; }
1000
+ applyLedger(ledger.record(finalX.settle?.transaction || finalX._responseId || receipt?.tx || requestReceiptId, final));
992
1001
  rememberSpend();
993
1002
  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
1003
  };
@@ -996,7 +1005,7 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
996
1005
  if (isPaidPost && response.ok && upCt.includes('application/json')) {
997
1006
  let data = null;
998
1007
  try { data = await response.clone().json(); } catch { /* not JSON after all */ }
999
- meterFinal({ ...data?.x402, usage: data?.usage, _completedResponse: response.ok });
1008
+ meterFinal({ ...data?.x402, usage: data?.usage, _responseId: data?.id, _completedResponse: response.ok });
1000
1009
  if (data?.object === 'chat.completion') {
1001
1010
  if (statsOff) {
1002
1011
  // The gateway already dropped its half; drop ours, and any block an
@@ -0,0 +1,22 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ export function receiptLedger(file, base = {}) {
4
+ let state = { base, receipts: {} };
5
+ try { const saved=JSON.parse(fs.readFileSync(file,'utf8')); if(saved.receipts && saved.base)state=saved; } catch {}
6
+ const totals = () => {
7
+ const billing={chargedUsd:state.base.billing?.chargedUsd || 0,calls:state.base.billing?.calls || 0};
8
+ const comparison={spentUsd:state.base.comparison?.spentUsd || 0,directUsd:state.base.comparison?.directUsd || 0,calls:state.base.comparison?.calls || 0};
9
+ for(const r of Object.values(state.receipts)) {
10
+ billing.chargedUsd+=r.charged;billing.calls++;
11
+ if(r.direct!==null){comparison.spentUsd+=r.charged;comparison.directUsd+=r.direct;comparison.calls++;}
12
+ }
13
+ return {billing,comparison};
14
+ };
15
+ return { totals, record(id, receipt) {
16
+ if(!id || typeof receipt.charged!=='number' || !Number.isFinite(receipt.charged) || receipt.charged<0) return totals();
17
+ state.receipts[id]={charged:receipt.charged,direct:typeof receipt.direct==='number'&&Number.isFinite(receipt.direct)&&receipt.direct>=0?receipt.direct:null};
18
+ fs.mkdirSync(path.dirname(file),{recursive:true});
19
+ const tmp=file+'.tmp';fs.writeFileSync(tmp,JSON.stringify(state),{mode:0o600});fs.renameSync(tmp,file);
20
+ return totals();
21
+ }};
22
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.51.11",
3
+ "version": "0.51.12",
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",