openzoo 0.50.61 → 0.50.62
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/cursorbackend.js +24 -0
- package/lib/proxy.js +32 -1
- package/package.json +1 -1
package/lib/cursorbackend.js
CHANGED
|
@@ -2944,6 +2944,30 @@ async function handleHijackedPodHttp(req, res, full, body, log) {
|
|
|
2944
2944
|
}
|
|
2945
2945
|
if (!path0.startsWith('/api/')) return false;
|
|
2946
2946
|
const name = path0.slice('/api/'.length);
|
|
2947
|
+
// Revive: the minutely cron (scripts/revive-bots.sh) calls this after it has
|
|
2948
|
+
// made sure the hijack + app are alive. Every non-group bot gets a wakeup
|
|
2949
|
+
// timer if it lost one; nothing is spawned, nothing already armed is touched.
|
|
2950
|
+
if (name === 'ozRevive') {
|
|
2951
|
+
let parsed = {};
|
|
2952
|
+
try { parsed = JSON.parse(String(body || '{}')); } catch { parsed = {}; }
|
|
2953
|
+
const every = String(parsed.every || '5m');
|
|
2954
|
+
const wakes = readWakeups(HOME);
|
|
2955
|
+
const list = (cachedAgentList() || []).filter((a) => a?.id && !a.isGroup);
|
|
2956
|
+
const armed = [];
|
|
2957
|
+
const already = [];
|
|
2958
|
+
for (const a of list) {
|
|
2959
|
+
if (wakes[a.id]) { already.push(a.id); continue; }
|
|
2960
|
+
try {
|
|
2961
|
+
scheduleAgentWakeup(a.id, { every, prompt: parsed.prompt });
|
|
2962
|
+
armed.push({ id: a.id, name: a.name });
|
|
2963
|
+
} catch (e) {
|
|
2964
|
+
log(`cursor-backend: ozRevive ${a.id} ${e.message}`);
|
|
2965
|
+
}
|
|
2966
|
+
}
|
|
2967
|
+
log(`cursor-backend: ozRevive total=${list.length} armed=${armed.length} already=${already.length} every=${every}`);
|
|
2968
|
+
jsonSend(res, { ok: true, total: list.length, armed, already: already.length, every });
|
|
2969
|
+
return true;
|
|
2970
|
+
}
|
|
2947
2971
|
if (name === 'getHostStatus') {
|
|
2948
2972
|
jsonSend(res, { status: 'ready', ready: true, state: 'ready', hostStatus: 'ready' });
|
|
2949
2973
|
log('cursor-backend: -> getHostStatus ready');
|
package/lib/proxy.js
CHANGED
|
@@ -304,6 +304,18 @@ function replayPut(key, data, settle) {
|
|
|
304
304
|
*/
|
|
305
305
|
export async function startProxy({ silent = false, requireToken = null, sessionMaxUsd = null, autoTunnel = false } = {}) {
|
|
306
306
|
const client = new PayClient();
|
|
307
|
+
// HOUSE OUTAGE GATE. Measured 2026-09-01: TOKEN payments settled on chain
|
|
308
|
+
// (tx 3rwy5z…) and the gateway still answered 402 because ITS upstream
|
|
309
|
+
// (OpenRouter) was out of credits. Paying again buys nothing, and the
|
|
310
|
+
// "wallet underfunded" copy blamed the user's burner. When the paid 402 says
|
|
311
|
+
// upstream credits, stop paying for a minute and say what is actually wrong.
|
|
312
|
+
let upstreamOutageUntil = 0;
|
|
313
|
+
let upstreamOutageMsg = '';
|
|
314
|
+
const upstreamOutage = (body) => {
|
|
315
|
+
const m = String(body?.error?.message || body?.message || '');
|
|
316
|
+
const src = String(body?.error?.metadata?.limit_source || '');
|
|
317
|
+
return /openrouter_credits/i.test(src) || /insufficient credits/i.test(m);
|
|
318
|
+
};
|
|
307
319
|
const log = silent ? () => {} : (...a) => console.log(...a);
|
|
308
320
|
// ALWAYS-ON, BUT NEVER INTO A HARNESS'S TERMINAL. `silent` means another
|
|
309
321
|
// process (openzoo claude, the editor launcher) owns stdio — printing request
|
|
@@ -684,6 +696,11 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
|
|
|
684
696
|
}
|
|
685
697
|
|
|
686
698
|
try {
|
|
699
|
+
if (Date.now() < upstreamOutageUntil) {
|
|
700
|
+
log('upstream outage: not paying (gate)');
|
|
701
|
+
jsonErr(res, 503, upstreamOutageMsg);
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
687
704
|
const result = await client.fetch(url, init);
|
|
688
705
|
const { response, paid, receipt, accept } = result;
|
|
689
706
|
if (paid && receipt) {
|
|
@@ -786,8 +803,22 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
|
|
|
786
803
|
if (response.status === 402) {
|
|
787
804
|
let quoted = '';
|
|
788
805
|
let usd;
|
|
806
|
+
let q402 = null;
|
|
807
|
+
try { q402 = await response.clone().json(); } catch { q402 = null; }
|
|
808
|
+
if (paid && upstreamOutage(q402)) {
|
|
809
|
+
const settle = q402?.x402?.settle;
|
|
810
|
+
const tx = settle?.transaction || result.receipt?.tx || '';
|
|
811
|
+
upstreamOutageMsg = 'openzoo gateway upstream is out of credits (OpenRouter: "Insufficient credits"). '
|
|
812
|
+
+ `Your payment settled${tx ? ` (tx ${tx})` : ''} — this is NOT your wallet. `
|
|
813
|
+
+ 'The gateway operator must top up OpenRouter or route this model to another upstream. '
|
|
814
|
+
+ 'Pausing paid retries for 60s.';
|
|
815
|
+
upstreamOutageUntil = Date.now() + 60_000;
|
|
816
|
+
log(`upstream outage: ${upstreamOutageMsg.slice(0, 120)}`);
|
|
817
|
+
jsonErr(res, 503, upstreamOutageMsg);
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
789
820
|
try {
|
|
790
|
-
const q =
|
|
821
|
+
const q = q402;
|
|
791
822
|
usd = Number(q?.accepts?.[0]?.extra?.billedUsd);
|
|
792
823
|
if (Number.isFinite(usd)) quoted = ` This call needs ≈$${usd.toFixed(4)}.`;
|
|
793
824
|
} catch { /* body was not the quote after all */ }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.50.
|
|
3
|
+
"version": "0.50.62",
|
|
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",
|