openzoo 0.48.28 → 0.48.29

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/lib/launch.js CHANGED
@@ -170,7 +170,10 @@ export async function launchClaude(argv) {
170
170
  + 'const tk=Number(sp.tokensApprox)||0;'
171
171
  + 'const ht=tk>=1e6?(tk/1e6).toFixed(1)+"M":tk>=1e3?Math.round(tk/1e3)+"k":String(tk);'
172
172
  + 'const spill=tk?(" \\u00b7 "+ht+" tok offloaded"):"";'
173
- + 'process.stdout.write("\\x1b[38;5;208m\\u25cf\\x1b[0m openzoo $"+(Number(j.spendUsd)||0).toFixed(4)+" "+(j.paidCalls||0)+" call"+((j.paidCalls||0)===1?"":"s")+spill+" \\u00b7 x402")}'
173
+ // "how can I easily see what credit i'm left" — asked by a user who
174
+ // could not tell whether x402 had charged them at all.
175
+ + 'const cr=(j.creditUsd==null)?"":(" \\u00b7 $"+Number(j.creditUsd).toFixed(2)+" credit");'
176
+ + 'process.stdout.write("\\x1b[38;5;208m\\u25cf\\x1b[0m openzoo $"+(Number(j.spendUsd)||0).toFixed(4)+" "+(j.paidCalls||0)+" call"+((j.paidCalls||0)===1?"":"s")+spill+cr+" \\u00b7 x402")}'
174
177
  + 'catch{process.stdout.write("\\x1b[38;5;208m\\u25cf\\x1b[0m openzoo \\u00b7 x402")}})\'\n');
175
178
  fs.chmodSync(scriptPath, 0o755);
176
179
  const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
package/lib/proxy.js CHANGED
@@ -447,6 +447,21 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
447
447
  let spillCalls = 0;
448
448
  let spilledChars = 0;
449
449
  let spillReuses = 0;
450
+ // CREDIT, CACHED. Users cannot tell prepaid credit from wallet balance and
451
+ // have to guess whether a call was even paid for ("I don't think x402 made me
452
+ // pay this at all"). The status line runs EVERY turn, so this is refreshed at
453
+ // most every 20s and served stale in between — a status line must never add
454
+ // latency to the thing it is describing.
455
+ let creditUsd = null;
456
+ let creditAt = 0;
457
+ const refreshCredit = () => {
458
+ if (Date.now() - creditAt < 20000) return;
459
+ creditAt = Date.now();
460
+ fetch(`${config.apiBase}/v1/credits`, { headers: withNamespace({}), signal: AbortSignal.timeout(4000) })
461
+ .then((r) => r.json())
462
+ .then((j) => { creditUsd = Number(j.balanceUsd) || 0; })
463
+ .catch(() => { /* advisory only */ });
464
+ };
450
465
  let tunnelError = null;
451
466
 
452
467
  const server = http.createServer(async (req, res) => {
@@ -540,6 +555,7 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
540
555
  {
541
556
  const p0 = (req.url || '').split('?')[0];
542
557
  if (req.method === 'GET' && (p0 === '/v1/info' || p0 === '/info')) {
558
+ refreshCredit();
543
559
  const self = viaTunnel && tunnelGate?.publicUrl
544
560
  ? `${tunnelGate.publicUrl}/v1`
545
561
  : `http://localhost:${config.port}/v1`;
@@ -560,6 +576,7 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
560
576
  reusedBinds: spillReuses,
561
577
  },
562
578
  spendUsd: sessionSpent,
579
+ creditUsd,
563
580
  paidCalls,
564
581
  mcp: `${self.replace(/\/v1$/, '')}/mcp`,
565
582
  upstream: config.apiBase,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.48.28",
3
+ "version": "0.48.29",
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",