openzoo 0.50.21 → 0.50.23
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 +250 -36
- package/lib/grokcli.js +4 -2
- package/package.json +1 -1
package/lib/cursorbackend.js
CHANGED
|
@@ -289,7 +289,34 @@ const RESP_DROP = new Set([
|
|
|
289
289
|
]);
|
|
290
290
|
|
|
291
291
|
const SNIFF_FILE = path.join(os.homedir(), '.openzoo', 'grokbot-sniff.jsonl');
|
|
292
|
-
|
|
292
|
+
const POD_FILE = path.join(os.homedir(), '.openzoo', 'grokbot-pod.json');
|
|
293
|
+
function loadPod() {
|
|
294
|
+
try { return JSON.parse(fs.readFileSync(POD_FILE, 'utf8')); } catch { return null; }
|
|
295
|
+
}
|
|
296
|
+
function savePod(p) {
|
|
297
|
+
if (!p?.agent) return;
|
|
298
|
+
try {
|
|
299
|
+
fs.mkdirSync(path.dirname(POD_FILE), { recursive: true });
|
|
300
|
+
fs.writeFileSync(POD_FILE, JSON.stringify(p));
|
|
301
|
+
} catch { /* */ }
|
|
302
|
+
}
|
|
303
|
+
let realPod = loadPod(); // { agent, vnc, token, p1340, ... } — persist so api2 timeout does not UUID-stub the sidebar
|
|
304
|
+
const AGENTS_FILE = path.join(os.homedir(), '.openzoo', 'grokbot-agents.json');
|
|
305
|
+
function loadAgents() {
|
|
306
|
+
try {
|
|
307
|
+
const a = JSON.parse(fs.readFileSync(AGENTS_FILE, 'utf8'));
|
|
308
|
+
return Array.isArray(a) ? a : null;
|
|
309
|
+
} catch { return null; }
|
|
310
|
+
}
|
|
311
|
+
function saveAgents(a) {
|
|
312
|
+
if (!Array.isArray(a) || !a.length) return;
|
|
313
|
+
try { fs.writeFileSync(AGENTS_FILE, JSON.stringify(a)); } catch { /* */ }
|
|
314
|
+
}
|
|
315
|
+
function cachedAgentList() {
|
|
316
|
+
const a = loadAgents();
|
|
317
|
+
if (a?.length) return a;
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
293
320
|
|
|
294
321
|
function sniffOn() { return process.env.OPENZOO_SNIFF === '1'; }
|
|
295
322
|
function sniffSelf() { return process.env.OZ_SNIFF_SELF || 'https://127.0.0.1:8443'; }
|
|
@@ -446,6 +473,7 @@ function rememberPod(fields, log) {
|
|
|
446
473
|
podId: String(fields[3] || ''),
|
|
447
474
|
};
|
|
448
475
|
sniffDump({ kind: 'pod', fields, realPod });
|
|
476
|
+
savePod(realPod);
|
|
449
477
|
log(`cursor-backend: SNIFF real pod ${realPod.agent}`);
|
|
450
478
|
return realPod;
|
|
451
479
|
}
|
|
@@ -470,7 +498,7 @@ async function sniffEnsureSandBox(req, res, body, host, full, log) {
|
|
|
470
498
|
const upstream = cursorUpstream(host);
|
|
471
499
|
const headers = copyReqHeaders(req, upstream);
|
|
472
500
|
const cap = await upstreamUnary({
|
|
473
|
-
host: upstream, path: full, method: req.method, headers, body, timeoutMs:
|
|
501
|
+
host: upstream, path: full, method: req.method, headers, body, timeoutMs: 60000,
|
|
474
502
|
});
|
|
475
503
|
const raw = inflateBody(cap.buf, cap.respHeaders);
|
|
476
504
|
const proto = unwrapConnect(raw);
|
|
@@ -564,6 +592,26 @@ async function proxyPodHttp(req, res, full, body, log) {
|
|
|
564
592
|
body,
|
|
565
593
|
timeoutMs: path0 === '/health' ? 8000 : 120000,
|
|
566
594
|
});
|
|
595
|
+
if (path0 === '/api/listAgents' && cap.status === 200) {
|
|
596
|
+
try {
|
|
597
|
+
const parsed = JSON.parse(String(inflateBody(cap.buf, cap.respHeaders)));
|
|
598
|
+
if (Array.isArray(parsed)) {
|
|
599
|
+
const merged = mergeAgentLists(parsed);
|
|
600
|
+
saveAgents(merged);
|
|
601
|
+
jsonSend(res, merged);
|
|
602
|
+
log(`cursor-backend: listAgents 200 merged n=${merged.length}`);
|
|
603
|
+
return true;
|
|
604
|
+
}
|
|
605
|
+
} catch { /* */ }
|
|
606
|
+
}
|
|
607
|
+
if (cap.status === 401 && path0 === '/api/listAgents') {
|
|
608
|
+
const cached = mergeAgentLists([]);
|
|
609
|
+
if (cached.length) {
|
|
610
|
+
jsonSend(res, cached);
|
|
611
|
+
log(`cursor-backend: listAgents 401 — cached ${cached.length} named agents`);
|
|
612
|
+
return true;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
567
615
|
writeCaptured(res, cap.status, cap.respHeaders, cap.buf);
|
|
568
616
|
const rec = {
|
|
569
617
|
kind: 'pod-http',
|
|
@@ -723,9 +771,33 @@ function expandUserPath(p) {
|
|
|
723
771
|
const transcripts = new Map();
|
|
724
772
|
const tailedAgents = new Set();
|
|
725
773
|
let lastSendEchoId = `oz-${Date.now()}`;
|
|
774
|
+
const TX_FILE = path.join(os.homedir(), '.openzoo', 'grokbot-transcripts.json');
|
|
775
|
+
function loadTranscripts() {
|
|
776
|
+
try {
|
|
777
|
+
const o = JSON.parse(fs.readFileSync(TX_FILE, 'utf8'));
|
|
778
|
+
for (const [id, t] of Object.entries(o || {})) {
|
|
779
|
+
transcripts.set(id, {
|
|
780
|
+
seq: Number(t.seq) || (t.entries || []).length,
|
|
781
|
+
entries: Array.isArray(t.entries) ? t.entries : [],
|
|
782
|
+
pulledRemote: false,
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
} catch { /* first run */ }
|
|
786
|
+
}
|
|
787
|
+
function saveTranscripts() {
|
|
788
|
+
try {
|
|
789
|
+
const o = {};
|
|
790
|
+
for (const [id, t] of transcripts) {
|
|
791
|
+
o[id] = { seq: t.seq, entries: t.entries.slice(-200) };
|
|
792
|
+
}
|
|
793
|
+
fs.mkdirSync(path.dirname(TX_FILE), { recursive: true });
|
|
794
|
+
fs.writeFileSync(TX_FILE, JSON.stringify(o));
|
|
795
|
+
} catch { /* */ }
|
|
796
|
+
}
|
|
797
|
+
loadTranscripts();
|
|
726
798
|
function agentTranscript(id) {
|
|
727
799
|
let t = transcripts.get(id);
|
|
728
|
-
if (!t) { t = { seq: 0, entries: [] }; transcripts.set(id, t); }
|
|
800
|
+
if (!t) { t = { seq: 0, entries: [], pulledRemote: false }; transcripts.set(id, t); }
|
|
729
801
|
return t;
|
|
730
802
|
}
|
|
731
803
|
function appendLine(agentId, role, text, extra = {}) {
|
|
@@ -761,18 +833,108 @@ function appendLine(agentId, role, text, extra = {}) {
|
|
|
761
833
|
};
|
|
762
834
|
}
|
|
763
835
|
t.entries.push(e);
|
|
836
|
+
saveTranscripts();
|
|
764
837
|
return e;
|
|
765
838
|
}
|
|
766
839
|
function fanoutLine(primaryId, role, text, extra = {}) {
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
840
|
+
// Only the addressed agent. Writing to every tailedAgents id mixed canvases
|
|
841
|
+
// so a new/empty chat showed someone else's thread.
|
|
842
|
+
return appendLine(primaryId, role, text, extra);
|
|
843
|
+
}
|
|
844
|
+
function mintLocalAgent(parsed = {}) {
|
|
845
|
+
const id = String(parsed.id || randomUUID());
|
|
846
|
+
const name = String(parsed.name || parsed.title || 'new chat');
|
|
847
|
+
const agent = {
|
|
848
|
+
id,
|
|
849
|
+
name,
|
|
850
|
+
description: String(parsed.description || ''),
|
|
851
|
+
title: String(parsed.title || name),
|
|
852
|
+
origin: parsed.origin || 'user',
|
|
853
|
+
createdAt: Date.now(),
|
|
854
|
+
updatedAt: Date.now(),
|
|
855
|
+
avatarShape: parsed.avatarShape || null,
|
|
856
|
+
avatarColor: parsed.avatarColor || null,
|
|
857
|
+
path: `/local/${id}`,
|
|
858
|
+
};
|
|
859
|
+
const list = cachedAgentList() || [];
|
|
860
|
+
if (!list.some((a) => a.id === id)) list.unshift(agent);
|
|
861
|
+
else {
|
|
862
|
+
const i = list.findIndex((a) => a.id === id);
|
|
863
|
+
list[i] = { ...list[i], ...agent };
|
|
864
|
+
}
|
|
865
|
+
saveAgents(list);
|
|
866
|
+
agentTranscript(id);
|
|
867
|
+
return agent;
|
|
868
|
+
}
|
|
869
|
+
function mergeAgentLists(remote) {
|
|
870
|
+
const local = cachedAgentList() || [];
|
|
871
|
+
const seen = new Set();
|
|
872
|
+
const out = [];
|
|
873
|
+
for (const a of [...local, ...(Array.isArray(remote) ? remote : [])]) {
|
|
874
|
+
if (!a?.id || seen.has(a.id)) continue;
|
|
875
|
+
seen.add(a.id);
|
|
876
|
+
out.push(a);
|
|
877
|
+
}
|
|
878
|
+
return out;
|
|
771
879
|
}
|
|
772
880
|
function gatewayEntry(e) {
|
|
773
|
-
const { seq, ...rest } = e;
|
|
881
|
+
const { seq, pulledRemote, ...rest } = e;
|
|
774
882
|
return rest;
|
|
775
883
|
}
|
|
884
|
+
async function podJson(path0, bodyObj, log) {
|
|
885
|
+
if (!realPod?.agent) return null;
|
|
886
|
+
let agent;
|
|
887
|
+
try { agent = new URL(realPod.agent); } catch { return null; }
|
|
888
|
+
const headers = {
|
|
889
|
+
'content-type': 'application/json',
|
|
890
|
+
accept: 'application/json',
|
|
891
|
+
authorization: `Bearer ${realPod.accessToken || realPod.token || ''}`,
|
|
892
|
+
'x-anyrun-network-token': realPod.token || '',
|
|
893
|
+
'x-sand-slim-avatars': '1',
|
|
894
|
+
};
|
|
895
|
+
try {
|
|
896
|
+
const cap = await upstreamUnary({
|
|
897
|
+
host: agent.hostname,
|
|
898
|
+
path: path0,
|
|
899
|
+
method: 'POST',
|
|
900
|
+
headers,
|
|
901
|
+
body: Buffer.from(JSON.stringify(bodyObj || {})),
|
|
902
|
+
timeoutMs: 20000,
|
|
903
|
+
});
|
|
904
|
+
if (cap.status !== 200) {
|
|
905
|
+
log(`cursor-backend: podJson ${path0} ${cap.status}`);
|
|
906
|
+
return null;
|
|
907
|
+
}
|
|
908
|
+
const raw = inflateBody(cap.buf, cap.respHeaders);
|
|
909
|
+
return JSON.parse(String(raw));
|
|
910
|
+
} catch (e) {
|
|
911
|
+
log(`cursor-backend: podJson ${path0} ${e.message}`);
|
|
912
|
+
return null;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
function ingestRemoteEntries(agentId, entries) {
|
|
916
|
+
if (!Array.isArray(entries) || !entries.length) return 0;
|
|
917
|
+
const t = agentTranscript(agentId);
|
|
918
|
+
const have = new Set(t.entries.map((e) => e.id || e.clientNonce || '').filter(Boolean));
|
|
919
|
+
const incoming = [];
|
|
920
|
+
for (const e of entries) {
|
|
921
|
+
if (!e || typeof e !== 'object') continue;
|
|
922
|
+
const k = e.id || e.clientNonce || '';
|
|
923
|
+
if (k && have.has(k)) continue;
|
|
924
|
+
incoming.push(e);
|
|
925
|
+
}
|
|
926
|
+
if (!incoming.length) return 0;
|
|
927
|
+
const local = t.entries;
|
|
928
|
+
t.entries = [];
|
|
929
|
+
t.seq = 0;
|
|
930
|
+
for (const e of [...incoming, ...local]) {
|
|
931
|
+
t.seq += 1;
|
|
932
|
+
const { seq: _s, ...rest } = e;
|
|
933
|
+
t.entries.push({ ...rest, seq: t.seq });
|
|
934
|
+
}
|
|
935
|
+
saveTranscripts();
|
|
936
|
+
return incoming.length;
|
|
937
|
+
}
|
|
776
938
|
|
|
777
939
|
function promptFromSendBody(raw) {
|
|
778
940
|
let obj = raw;
|
|
@@ -1223,12 +1385,56 @@ async function handleHijackedPodHttp(req, res, full, body, log) {
|
|
|
1223
1385
|
'getBotTemplateExportPolicy', 'getTeachRecordingStatus', 'isGlobalSearchEnabled',
|
|
1224
1386
|
'isEgressTunnelAvailable', 'listBoxMcpServers', 'getHostStatus',
|
|
1225
1387
|
'setWindowFocused', 'getAgentAutomations',
|
|
1226
|
-
'
|
|
1227
|
-
'updateAgent', 'deleteAgents', 'duplicateAgent', 'kickstartAgent',
|
|
1388
|
+
'createGroup', 'setGroupMembers',
|
|
1228
1389
|
'interruptAgentRun', 'requestDiskSaverAudit', 'broadcastToAgents',
|
|
1229
1390
|
'setAgentUnread', 'setAgentHiddenFromSidebar', 'setAgentNotificationsEnabled',
|
|
1230
1391
|
'setAgentNotifyOnUpdates', 'setAgentAvatarBytes', 'getAgentAvatar',
|
|
1231
1392
|
]);
|
|
1393
|
+
// create/delete stay LOCAL — 1340 createAgent 401s on a stale cached token
|
|
1394
|
+
// and the UI then never grows a sidebar row or clears the canvas.
|
|
1395
|
+
if (name === 'createAgent' || name === 'createAgentFromTemplate' || name === 'duplicateAgent') {
|
|
1396
|
+
let parsed = {};
|
|
1397
|
+
try { parsed = JSON.parse(String(body || '{}')); } catch { parsed = {}; }
|
|
1398
|
+
if (name === 'duplicateAgent' && parsed.id) {
|
|
1399
|
+
const src = (cachedAgentList() || []).find((a) => a.id === parsed.id) || {};
|
|
1400
|
+
parsed = { ...src, id: undefined, name: `${src.name || 'chat'} copy` };
|
|
1401
|
+
}
|
|
1402
|
+
const agent = mintLocalAgent(parsed);
|
|
1403
|
+
jsonSend(res, { agent, id: agent.id, ...agent });
|
|
1404
|
+
ssePush('agents', { action: 'created', agent });
|
|
1405
|
+
log(`cursor-backend: createAgent local id=${agent.id} name=${JSON.stringify(agent.name)}`);
|
|
1406
|
+
return true;
|
|
1407
|
+
}
|
|
1408
|
+
if (name === 'updateAgent') {
|
|
1409
|
+
let parsed = {};
|
|
1410
|
+
try { parsed = JSON.parse(String(body || '{}')); } catch { parsed = {}; }
|
|
1411
|
+
const agent = mintLocalAgent(parsed);
|
|
1412
|
+
jsonSend(res, { agent, ...agent });
|
|
1413
|
+
log(`cursor-backend: updateAgent local id=${agent.id}`);
|
|
1414
|
+
return true;
|
|
1415
|
+
}
|
|
1416
|
+
if (name === 'deleteAgents') {
|
|
1417
|
+
let parsed = {};
|
|
1418
|
+
try { parsed = JSON.parse(String(body || '{}')); } catch { parsed = {}; }
|
|
1419
|
+
const ids = new Set([].concat(parsed.ids || parsed.id || []).map(String));
|
|
1420
|
+
const next = (cachedAgentList() || []).filter((a) => !ids.has(a.id));
|
|
1421
|
+
saveAgents(next);
|
|
1422
|
+
for (const id of ids) transcripts.delete(id);
|
|
1423
|
+
jsonSend(res, { ok: true, deleted: [...ids] });
|
|
1424
|
+
log(`cursor-backend: deleteAgents n=${ids.size}`);
|
|
1425
|
+
return true;
|
|
1426
|
+
}
|
|
1427
|
+
if (name === 'listAgents') {
|
|
1428
|
+
if (!sniffOn() && realPod?.agent) {
|
|
1429
|
+
const proxied = await proxyPodHttp(req, res, full, body, log);
|
|
1430
|
+
if (proxied) return true;
|
|
1431
|
+
}
|
|
1432
|
+
const list = mergeAgentLists([]);
|
|
1433
|
+
jsonSend(res, list);
|
|
1434
|
+
log(`cursor-backend: listAgents local n=${list.length}`);
|
|
1435
|
+
return true;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1232
1438
|
if (!sniffOn() && realPod?.agent && roster.has(name)) {
|
|
1233
1439
|
return proxyPodHttp(req, res, full, body, log);
|
|
1234
1440
|
}
|
|
@@ -1289,24 +1495,25 @@ async function handleHijackedPodHttp(req, res, full, body, log) {
|
|
|
1289
1495
|
if (name === 'getAgentTranscriptTail' || name === 'getAgentTranscriptWindow' || name === 'openAgentTail') {
|
|
1290
1496
|
let parsed = {};
|
|
1291
1497
|
try { parsed = JSON.parse(String(body || '{}')); } catch { parsed = {}; }
|
|
1292
|
-
|
|
1498
|
+
const id = String(parsed.id || parsed.agentId || 'openzoo');
|
|
1293
1499
|
tailedAgents.add(id);
|
|
1294
|
-
|
|
1295
|
-
if (!t.
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
}
|
|
1500
|
+
const t = agentTranscript(id);
|
|
1501
|
+
if (!t.pulledRemote && realPod?.agent) {
|
|
1502
|
+
const remote = await podJson('/api/getAgentTranscriptTail', {
|
|
1503
|
+
id, agentId: id, limit: 200, beforeSeq: parsed.beforeSeq,
|
|
1504
|
+
}, log);
|
|
1505
|
+
const n = ingestRemoteEntries(id, remote?.entries);
|
|
1506
|
+
t.pulledRemote = true;
|
|
1507
|
+
if (n) log(`cursor-backend: hydrated ${id} +${n} from 1340`);
|
|
1299
1508
|
}
|
|
1509
|
+
const t2 = agentTranscript(id);
|
|
1300
1510
|
const limit = Math.min(Number(parsed.limit) || 50, 200);
|
|
1301
1511
|
const before = parsed.beforeSeq != null ? Number(parsed.beforeSeq) : Infinity;
|
|
1302
|
-
const sliced =
|
|
1512
|
+
const sliced = t2.entries.filter((e) => e.seq < before).slice(-limit);
|
|
1303
1513
|
const page = { entries: sliced.map(gatewayEntry) };
|
|
1304
|
-
if (
|
|
1305
|
-
// Live pod returns RAW {entries, nextBeforeSeq} — no CVr envelope
|
|
1306
|
-
// (measured 1340 getAgentTranscriptTail 2026-08-29). Wrapping {status,value}
|
|
1307
|
-
// made transcript-page validation fail and the canvas stayed empty.
|
|
1514
|
+
if (t2.entries.length > sliced.length && sliced.length) page.nextBeforeSeq = sliced[0].seq;
|
|
1308
1515
|
jsonSend(res, page);
|
|
1309
|
-
log(`cursor-backend: -> transcript ${id} n=${sliced.length}/${
|
|
1516
|
+
log(`cursor-backend: -> transcript ${id} n=${sliced.length}/${t2.seq}`);
|
|
1310
1517
|
return true;
|
|
1311
1518
|
}
|
|
1312
1519
|
if (name === 'promptAcceptanceStatus') {
|
|
@@ -1334,7 +1541,8 @@ async function handleHijackedPodHttp(req, res, full, body, log) {
|
|
|
1334
1541
|
getHostSettings: { settings: {} },
|
|
1335
1542
|
setHostSettings: { ok: true },
|
|
1336
1543
|
setBoxSecrets: { ok: true },
|
|
1337
|
-
listAgents:
|
|
1544
|
+
listAgents: cachedAgentList()
|
|
1545
|
+
|| [...new Set([...transcripts.keys(), ...tailedAgents])].map((id) => ({ id, name: id, status: 'ready' })),
|
|
1338
1546
|
getAgentTranscriptTail: { tail: '', lines: [], dropped: false, ok: true },
|
|
1339
1547
|
getTeachRecordingStatus: { recording: false },
|
|
1340
1548
|
getTrays: { trays: [] },
|
|
@@ -1551,25 +1759,31 @@ export function startCursorBackend({ port = 8443, models, log = () => {} } = {})
|
|
|
1551
1759
|
// with OUR box so Grok Bot's UI wires to our sandbox; everything else
|
|
1552
1760
|
// still passes through so the app loads normally.
|
|
1553
1761
|
if (/GrokBotService\/(EnsureSandBox|WatchSandBoxMigration)/.test(full) && process.env.OZ_HIJACK_POD) {
|
|
1554
|
-
if (/WatchSandBoxMigration/.test(full) && realPod) {
|
|
1555
|
-
const payload = rewrittenBox();
|
|
1556
|
-
res.writeHead(200, {
|
|
1557
|
-
'content-type': 'application/connect+proto',
|
|
1558
|
-
'grpc-status': '0',
|
|
1559
|
-
...CORS,
|
|
1560
|
-
});
|
|
1561
|
-
const end = Buffer.from('{}');
|
|
1562
|
-
const h = Buffer.alloc(5); h.writeUInt8(0x02, 0); h.writeUInt32BE(end.length, 1);
|
|
1563
|
-
res.end(Buffer.concat([envelope(payload), h, end]));
|
|
1564
|
-
log('cursor-backend: -> WatchSandBoxMigration ready (hijack, real roster)');
|
|
1565
|
-
return;
|
|
1566
|
-
}
|
|
1567
1762
|
try {
|
|
1568
1763
|
process.env.OZ_SNIFF_SELF = process.env.OZ_SNIFF_SELF || 'https://127.0.0.1:8443';
|
|
1569
1764
|
await sniffEnsureSandBox(req, res, body, host, full, log);
|
|
1570
1765
|
log('cursor-backend: -> HIJACKED EnsureSandBox -> our box (roster from real 1340)');
|
|
1571
1766
|
return;
|
|
1572
1767
|
} catch (e) {
|
|
1768
|
+
if (realPod?.agent) {
|
|
1769
|
+
log(`cursor-backend: EnsureSandBox discover failed (${e.message}) — cached 1340 roster`);
|
|
1770
|
+
const payload = rewrittenBox();
|
|
1771
|
+
const reqCt = String(req.headers['content-type'] || '');
|
|
1772
|
+
if (/WatchSandBoxMigration/.test(full) || reqCt.includes('connect+proto')) {
|
|
1773
|
+
res.writeHead(200, {
|
|
1774
|
+
'content-type': 'application/connect+proto',
|
|
1775
|
+
'grpc-status': '0',
|
|
1776
|
+
...CORS,
|
|
1777
|
+
});
|
|
1778
|
+
const end = Buffer.from('{}');
|
|
1779
|
+
const h = Buffer.alloc(5); h.writeUInt8(0x02, 0); h.writeUInt32BE(end.length, 1);
|
|
1780
|
+
res.end(Buffer.concat([envelope(payload), h, end]));
|
|
1781
|
+
} else {
|
|
1782
|
+
res.writeHead(200, { 'content-type': 'application/proto', ...CORS });
|
|
1783
|
+
res.end(payload);
|
|
1784
|
+
}
|
|
1785
|
+
return;
|
|
1786
|
+
}
|
|
1573
1787
|
log(`cursor-backend: EnsureSandBox discover failed (${e.message}) — env box`);
|
|
1574
1788
|
}
|
|
1575
1789
|
let pod;
|
package/lib/grokcli.js
CHANGED
|
@@ -177,8 +177,10 @@ export async function runBot(argv = []) {
|
|
|
177
177
|
console.error(' EnsureSandBox HIJACKED here; StreamUnifiedChat -> :8402 (x402)');
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
|
|
181
|
-
|
|
180
|
+
if (!argv.includes('--no-quit') && process.env.OZ_NO_QUIT !== '1') {
|
|
181
|
+
try { execSync('osascript -e \'tell application "Grok Bot" to quit\'', { stdio: 'ignore' }); } catch { /* ok */ }
|
|
182
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
183
|
+
}
|
|
182
184
|
|
|
183
185
|
console.error('openzoo: launching Grok Bot');
|
|
184
186
|
console.error(` CURSOR_API_BASE_URL=${url}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.50.
|
|
3
|
+
"version": "0.50.23",
|
|
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",
|