openzoo 0.48.28 → 0.48.30
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 +11 -1
- package/lib/proxy.js +25 -0
- package/package.json +1 -1
package/lib/launch.js
CHANGED
|
@@ -170,7 +170,17 @@ 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
|
-
|
|
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
|
+
// The headline: what the same calls would have cost on OpenRouter.
|
|
177
|
+
// ALWAYS shown, 4dp, even at 1.0000. Hiding it when there is no saving
|
|
178
|
+
// reads as "no data"; printing 1.0000x says "measured, and it is level"
|
|
179
|
+
// — which is the difference between an omission and a fact.
|
|
180
|
+
+ 'const sx=Number(j.savingX);const sv=Number(j.savedUsd)||0;'
|
|
181
|
+
+ 'const col=(sx>1)?"\\x1b[32m":"\\x1b[90m";'
|
|
182
|
+
+ 'const save=(sx==null||!isFinite(sx))?"":(" \\u00b7 "+col+sx.toFixed(4)+"x vs direct"+(sv>0?(" ($"+sv.toFixed(4)+" saved)"):"")+"\\x1b[0m");'
|
|
183
|
+
+ '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")+save+spill+cr+" \\u00b7 x402")}'
|
|
174
184
|
+ 'catch{process.stdout.write("\\x1b[38;5;208m\\u25cf\\x1b[0m openzoo \\u00b7 x402")}})\'\n');
|
|
175
185
|
fs.chmodSync(scriptPath, 0o755);
|
|
176
186
|
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,15 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
|
|
|
560
576
|
reusedBinds: spillReuses,
|
|
561
577
|
},
|
|
562
578
|
spendUsd: sessionSpent,
|
|
579
|
+
creditUsd,
|
|
580
|
+
// WHAT THE SAME CALLS WOULD HAVE COST DIRECT. Spend on its own is a
|
|
581
|
+
// bill; spend beside the counterfactual is the product. The receipt
|
|
582
|
+
// already carries directUsd per call — it simply never reached the
|
|
583
|
+
// status line, so the one number that justifies the tool was the one
|
|
584
|
+
// the user could not see.
|
|
585
|
+
directUsd: sessionDirect,
|
|
586
|
+
savedUsd: Math.max(0, sessionDirect - sessionSpent),
|
|
587
|
+
savingX: sessionSpent > 0 ? Number((sessionDirect / sessionSpent).toFixed(4)) : null,
|
|
563
588
|
paidCalls,
|
|
564
589
|
mcp: `${self.replace(/\/v1$/, '')}/mcp`,
|
|
565
590
|
upstream: config.apiBase,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.48.
|
|
3
|
+
"version": "0.48.30",
|
|
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",
|