openzoo 0.50.30 → 0.50.32
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 +64 -13
- package/lib/grokcli.js +9 -1
- package/package.json +1 -1
package/lib/cursorbackend.js
CHANGED
|
@@ -1039,7 +1039,7 @@ function loadTranscripts() {
|
|
|
1039
1039
|
}
|
|
1040
1040
|
} catch { /* first run */ }
|
|
1041
1041
|
}
|
|
1042
|
-
function
|
|
1042
|
+
function saveTranscriptsNow() {
|
|
1043
1043
|
try {
|
|
1044
1044
|
const o = {};
|
|
1045
1045
|
for (const [id, t] of transcripts) {
|
|
@@ -1049,6 +1049,16 @@ function saveTranscripts() {
|
|
|
1049
1049
|
fs.writeFileSync(TX_FILE, JSON.stringify(o));
|
|
1050
1050
|
} catch { /* */ }
|
|
1051
1051
|
}
|
|
1052
|
+
let saveTxTimer;
|
|
1053
|
+
function saveTranscripts() {
|
|
1054
|
+
clearTimeout(saveTxTimer);
|
|
1055
|
+
saveTxTimer = setTimeout(saveTranscriptsNow, 250);
|
|
1056
|
+
}
|
|
1057
|
+
function flushTranscripts() {
|
|
1058
|
+
clearTimeout(saveTxTimer);
|
|
1059
|
+
saveTranscriptsNow();
|
|
1060
|
+
}
|
|
1061
|
+
process.once('beforeExit', flushTranscripts);
|
|
1052
1062
|
loadTranscripts();
|
|
1053
1063
|
function agentTranscript(id) {
|
|
1054
1064
|
let t = transcripts.get(id);
|
|
@@ -1141,6 +1151,16 @@ function stripSpendFooter(s) {
|
|
|
1141
1151
|
return String(s || '').replace(/\n{2,}this call \$[\d.]+[\s\S]*$/i, '').trimEnd();
|
|
1142
1152
|
}
|
|
1143
1153
|
|
|
1154
|
+
function entryPlainText(v) {
|
|
1155
|
+
if (v == null) return '';
|
|
1156
|
+
if (typeof v === 'string' || typeof v === 'number') return String(v);
|
|
1157
|
+
if (Array.isArray(v)) return v.map(entryPlainText).filter(Boolean).join('\n');
|
|
1158
|
+
if (typeof v === 'object') {
|
|
1159
|
+
return entryPlainText(v.content ?? v.text ?? v.message ?? v.prompt ?? v.value ?? '');
|
|
1160
|
+
}
|
|
1161
|
+
return '';
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1144
1164
|
/** Prior turns for zooComplete. sendPrompt used to POST only the latest user
|
|
1145
1165
|
* line, so the model said each thread starts blank while the UI still showed
|
|
1146
1166
|
* the canvas. Skip the last user echo of `currentPrompt` (already appended). */
|
|
@@ -1152,13 +1172,12 @@ function historyMessages(agentId, currentPrompt) {
|
|
|
1152
1172
|
if (!e || typeof e !== 'object') continue;
|
|
1153
1173
|
let role = null;
|
|
1154
1174
|
let text = '';
|
|
1155
|
-
if (e.kind === 'send-message' || e.role === 'assistant') {
|
|
1156
|
-
|
|
1157
|
-
text = typeof m === 'string' ? m : String(m?.content || m?.text || '');
|
|
1175
|
+
if (e.kind === 'send-message' || e.role === 'assistant' || e.kind === 'assistant') {
|
|
1176
|
+
text = entryPlainText(e.message ?? e.content ?? e.text);
|
|
1158
1177
|
role = 'assistant';
|
|
1159
1178
|
text = stripSpendFooter(text);
|
|
1160
|
-
} else if (e.kind === 'message' || e.role === 'user') {
|
|
1161
|
-
text =
|
|
1179
|
+
} else if (e.kind === 'message' || e.role === 'user' || e.kind === 'user') {
|
|
1180
|
+
text = entryPlainText(e.content ?? e.message ?? e.text ?? e.prompt);
|
|
1162
1181
|
role = 'user';
|
|
1163
1182
|
}
|
|
1164
1183
|
text = String(text || '').trim();
|
|
@@ -1172,12 +1191,28 @@ function historyMessages(agentId, currentPrompt) {
|
|
|
1172
1191
|
const kept = [];
|
|
1173
1192
|
for (let i = out.length - 1; i >= 0; i--) {
|
|
1174
1193
|
chars += String(out[i].content).length;
|
|
1175
|
-
if (chars >
|
|
1194
|
+
if (chars > 120_000 && kept.length) break;
|
|
1176
1195
|
kept.push(out[i]);
|
|
1177
1196
|
}
|
|
1178
1197
|
kept.reverse();
|
|
1179
1198
|
return kept;
|
|
1180
1199
|
}
|
|
1200
|
+
|
|
1201
|
+
async function ensureTranscriptHydrated(agentId, log) {
|
|
1202
|
+
const t = agentTranscript(agentId);
|
|
1203
|
+
if (t.pulledRemote || !realPod?.agent) return;
|
|
1204
|
+
try {
|
|
1205
|
+
const remote = await podJson('/api/getAgentTranscriptTail', {
|
|
1206
|
+
id: agentId, agentId, limit: 200,
|
|
1207
|
+
}, log);
|
|
1208
|
+
const entries = remote?.entries || remote?.value?.entries || remote?.lines || [];
|
|
1209
|
+
const n = ingestRemoteEntries(agentId, entries);
|
|
1210
|
+
if (n) log(`cursor-backend: zooComplete hydrate ${agentId} +${n} from 1340`);
|
|
1211
|
+
} catch (e) {
|
|
1212
|
+
log(`cursor-backend: zooComplete hydrate failed: ${e.message}`);
|
|
1213
|
+
}
|
|
1214
|
+
t.pulledRemote = true;
|
|
1215
|
+
}
|
|
1181
1216
|
async function podJson(path0, bodyObj, log) {
|
|
1182
1217
|
if (!realPod?.agent) return null;
|
|
1183
1218
|
let agent;
|
|
@@ -1527,7 +1562,8 @@ function zooTextFromMessage(msg, data) {
|
|
|
1527
1562
|
async function zooComplete(prompt, log, agentId, parsed = {}) {
|
|
1528
1563
|
const model = currentModel(agentId);
|
|
1529
1564
|
const helper = localExecSse.size > 0;
|
|
1530
|
-
|
|
1565
|
+
await ensureTranscriptHydrated(agentId, log);
|
|
1566
|
+
log(`cursor-backend: zoo POST :8402 model=${model} helper=${helper ? localExecSse.size : 0} hist=${historyMessages(agentId, prompt).length} ${JSON.stringify((prompt || '').slice(0, 60))}`);
|
|
1531
1567
|
|
|
1532
1568
|
const images = [];
|
|
1533
1569
|
const textFiles = [];
|
|
@@ -1593,19 +1629,34 @@ async function zooComplete(prompt, log, agentId, parsed = {}) {
|
|
|
1593
1629
|
let text = '';
|
|
1594
1630
|
const usedTools = [];
|
|
1595
1631
|
const zooPost = async (payload) => {
|
|
1632
|
+
const ms = Number(process.env.OPENZOO_ASK_TIMEOUT_MS || 10 * 60_000);
|
|
1596
1633
|
const post = () => fetch('http://127.0.0.1:8402/v1/chat/completions', {
|
|
1597
1634
|
method: 'POST',
|
|
1598
1635
|
headers: { 'content-type': 'application/json', authorization: 'Bearer sk-openzoo' },
|
|
1599
1636
|
body: JSON.stringify(payload),
|
|
1600
|
-
signal: AbortSignal.timeout(
|
|
1637
|
+
signal: AbortSignal.timeout(ms),
|
|
1601
1638
|
});
|
|
1602
|
-
let r
|
|
1639
|
+
let r;
|
|
1640
|
+
let lastErr;
|
|
1641
|
+
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
1642
|
+
try {
|
|
1643
|
+
r = await post();
|
|
1644
|
+
lastErr = null;
|
|
1645
|
+
break;
|
|
1646
|
+
} catch (e) {
|
|
1647
|
+
lastErr = e;
|
|
1648
|
+
log(`cursor-backend: zoo POST attempt=${attempt} ${e.message}`);
|
|
1649
|
+
if (attempt === 3) throw e;
|
|
1650
|
+
await new Promise((ok) => setTimeout(ok, 800 * attempt));
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
if (!r) throw lastErr || new Error('zoo POST failed');
|
|
1603
1654
|
if (r.status === 402) {
|
|
1604
1655
|
log('cursor-backend: x402 402 — dwell/retry');
|
|
1605
1656
|
await new Promise((ok) => setTimeout(ok, 2500));
|
|
1606
|
-
r = await post();
|
|
1657
|
+
try { r = await post(); } catch (e) { log(`cursor-backend: zoo POST after 402 ${e.message}`); }
|
|
1607
1658
|
}
|
|
1608
|
-
const data = await r.json();
|
|
1659
|
+
const data = await r.json().catch(() => ({}));
|
|
1609
1660
|
if (r.status === 402) {
|
|
1610
1661
|
const raw = zooTextFromMessage(data?.choices?.[0]?.message, data)
|
|
1611
1662
|
|| data?.error?.message
|
|
@@ -1836,6 +1887,7 @@ async function handleHijackedPodHttp(req, res, full, body, log) {
|
|
|
1836
1887
|
const attN = Array.isArray(parsed.attachmentPaths) ? parsed.attachmentPaths.length : 0;
|
|
1837
1888
|
log(`cursor-backend: >> sendPrompt agent=${agentId} keys=${Object.keys(parsed).join(',')} attachments=${attN} prompt=${JSON.stringify((prompt || '').slice(0, 80))}`);
|
|
1838
1889
|
lastSendEchoId = String(nonce);
|
|
1890
|
+
jsonSend(res, { accepted: true });
|
|
1839
1891
|
const userLine = fanoutLine(agentId, 'user', prompt, {
|
|
1840
1892
|
clientNonce: nonce,
|
|
1841
1893
|
requestId: nonce,
|
|
@@ -1844,7 +1896,6 @@ async function handleHijackedPodHttp(req, res, full, body, log) {
|
|
|
1844
1896
|
noteFocus(agentId);
|
|
1845
1897
|
bumpAgent(agentId, { preview: prompt, notify: false });
|
|
1846
1898
|
ssePush('transcript', { ...gatewayEntry(userLine), agentId });
|
|
1847
|
-
jsonSend(res, { accepted: true });
|
|
1848
1899
|
const modelCmd = /^\s*\/model(?:\s+(\S+))?\s*$/i.exec(prompt || '');
|
|
1849
1900
|
setImmediate(async () => {
|
|
1850
1901
|
let text = '';
|
package/lib/grokcli.js
CHANGED
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
import { spawn, execSync } from 'node:child_process';
|
|
19
19
|
import { existsSync, readFileSync, writeFileSync, copyFileSync, mkdirSync } from 'node:fs';
|
|
20
20
|
import { homedir } from 'node:os';
|
|
21
|
-
import { join } from 'node:path';
|
|
21
|
+
import { dirname, join } from 'node:path';
|
|
22
|
+
import { fileURLToPath } from 'node:url';
|
|
22
23
|
|
|
23
24
|
import { config } from './config.js';
|
|
24
25
|
|
|
@@ -168,6 +169,13 @@ export async function runBot(argv = []) {
|
|
|
168
169
|
console.error('openzoo: 8443 already bound — reusing it (', e.message, ')');
|
|
169
170
|
}
|
|
170
171
|
await new Promise((r) => setTimeout(r, 400));
|
|
172
|
+
let ver = '?';
|
|
173
|
+
try {
|
|
174
|
+
ver = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json'), 'utf8')).version;
|
|
175
|
+
} catch { /* */ }
|
|
176
|
+
process.on('uncaughtException', (e) => console.error('openzoo: uncaught', e?.message || e));
|
|
177
|
+
process.on('unhandledRejection', (e) => console.error('openzoo: rejection', e?.message || e));
|
|
178
|
+
console.error(`openzoo: grokbot hijack v${ver}`);
|
|
171
179
|
console.error(`openzoo: aiserver on ${url}`);
|
|
172
180
|
console.error(' oauth + /sand-box creds -> real api2');
|
|
173
181
|
if (sniff) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.50.
|
|
3
|
+
"version": "0.50.32",
|
|
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",
|