openzoo 0.48.45 → 0.48.47
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/proxy.js +45 -4
- package/package.json +1 -1
package/lib/proxy.js
CHANGED
|
@@ -66,20 +66,43 @@ async function readBody(req) {
|
|
|
66
66
|
return Buffer.concat(chunks);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
/** Pipe an upstream fetch Response to the client, unbuffered (SSE-safe).
|
|
70
|
-
|
|
69
|
+
/** Pipe an upstream fetch Response to the client, unbuffered (SSE-safe).
|
|
70
|
+
*
|
|
71
|
+
* `onReceipt` is called with the gateway's x402 block when it arrives. On a
|
|
72
|
+
* STREAMED call there is no JSON body to carry that block, so the gateway
|
|
73
|
+
* emits it as an SSE COMMENT (`: x402 {...}`) after the last frame — comments
|
|
74
|
+
* are discarded by every compliant client, so nothing downstream sees it, but
|
|
75
|
+
* without reading it here every spend and savings figure on the status line
|
|
76
|
+
* would silently read zero the moment real streaming was switched on.
|
|
77
|
+
*
|
|
78
|
+
* Sniffing NEVER delays a byte: each chunk is written to the client first and
|
|
79
|
+
* only then scanned. */
|
|
80
|
+
function relay(res, upstream, onReceipt) {
|
|
71
81
|
const headers = {};
|
|
72
82
|
upstream.headers.forEach((v, k) => {
|
|
73
83
|
if (!['transfer-encoding', 'connection', 'content-encoding', 'content-length'].includes(k)) headers[k] = v;
|
|
74
84
|
});
|
|
75
85
|
res.writeHead(upstream.status, headers);
|
|
76
86
|
if (!upstream.body) { res.end(); return Promise.resolve(); }
|
|
87
|
+
const sse = (upstream.headers.get('content-type') || '').includes('text/event-stream');
|
|
77
88
|
return new Promise((resolve) => {
|
|
78
89
|
const body = Readable.fromWeb(upstream.body);
|
|
79
90
|
body.on('error', () => res.destroy());
|
|
80
91
|
res.on('close', () => body.destroy());
|
|
81
92
|
body.on('end', resolve);
|
|
82
|
-
body.pipe(res);
|
|
93
|
+
if (!sse || typeof onReceipt !== 'function') { body.pipe(res); return; }
|
|
94
|
+
let pending = '';
|
|
95
|
+
body.on('data', (c) => {
|
|
96
|
+
res.write(c);
|
|
97
|
+
pending += c.toString('utf8');
|
|
98
|
+
const lines = pending.split('\n');
|
|
99
|
+
pending = lines.pop() ?? ''; // a comment can straddle two chunks
|
|
100
|
+
for (const line of lines) {
|
|
101
|
+
if (!line.startsWith(': x402 ')) continue;
|
|
102
|
+
try { onReceipt(JSON.parse(line.slice(7))); } catch { /* not our frame */ }
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
body.on('end', () => res.end());
|
|
83
106
|
});
|
|
84
107
|
}
|
|
85
108
|
|
|
@@ -1197,7 +1220,25 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
|
|
|
1197
1220
|
return;
|
|
1198
1221
|
}
|
|
1199
1222
|
}
|
|
1200
|
-
|
|
1223
|
+
// A STREAMED call is metered from the gateway's trailing SSE comment —
|
|
1224
|
+
// same figures the JSON path reads out of `data.x402`, same counters, so
|
|
1225
|
+
// the status line does not care which transport served the answer.
|
|
1226
|
+
await relay(res, response, (x) => {
|
|
1227
|
+
if (paid || typeof x?.billedUsd !== 'number') return;
|
|
1228
|
+
sessionSpent += x.billedUsd;
|
|
1229
|
+
sessionCogs += typeof x.cogsUsd === 'number' ? x.cogsUsd : x.billedUsd / MARKUP;
|
|
1230
|
+
sessionDirect += typeof x.directUsd === 'number' ? x.directUsd : x.billedUsd;
|
|
1231
|
+
if (typeof x.actualUsd === 'number' && x.actualUsd >= 0) { sessionActual += x.actualUsd; actualCalls += 1; }
|
|
1232
|
+
if (didSpill) {
|
|
1233
|
+
const lc = x.lecore || {};
|
|
1234
|
+
log(`spill priced (streamed): ${x.pricing} · basis ${x.counterfactualTokensUsed ?? '?'} tok vs sent ${lc.tokensBefore ?? '?'} -> ${lc.tokensAfter ?? '?'} · billed ${(x.billedUsd ?? 0).toFixed(5)} direct ${(x.directUsd ?? 0).toFixed(5)}`);
|
|
1235
|
+
spillSpend += x.billedUsd;
|
|
1236
|
+
spillDirect += typeof x.directUsd === 'number' ? x.directUsd : x.billedUsd;
|
|
1237
|
+
}
|
|
1238
|
+
paidCalls += 1;
|
|
1239
|
+
if (viaTunnel) tunnelSpent += x.billedUsd;
|
|
1240
|
+
say(`credit -> $${x.billedUsd.toFixed(6)} · session $${sessionSpent.toFixed(6)}`);
|
|
1241
|
+
});
|
|
1201
1242
|
} catch (err) {
|
|
1202
1243
|
if (err instanceof QuoteTooHighError) {
|
|
1203
1244
|
log(err.message);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.48.
|
|
3
|
+
"version": "0.48.47",
|
|
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",
|