openzoo 0.50.49 → 0.50.51
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 +15 -5
- package/lib/grokbotAccount.js +25 -0
- package/lib/grokcli.js +6 -6
- package/lib/ozSpendChip.js +132 -17
- package/package.json +1 -1
package/lib/cursorbackend.js
CHANGED
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
accountPodPath, accountAgentsPath, rosterForAccount, rosterForEvent,
|
|
42
42
|
readHouseRoster, houseAgentsPath, shapeAgent, agentBrief, briefFromName,
|
|
43
43
|
readWakeups, writeWakeups, shapeWakeup, parseWakeupEvery, wantsWakeupCron,
|
|
44
|
-
DEFAULT_WAKEUP_PROMPT,
|
|
44
|
+
DEFAULT_WAKEUP_PROMPT, addDeletedIds, filterDeleted,
|
|
45
45
|
} from './grokbotAccount.js';
|
|
46
46
|
import { formatSpendFooter, mergeTurnProof } from './spendProof.js';
|
|
47
47
|
import { prefixVisitorRichText } from './grokbotweb.js';
|
|
@@ -363,7 +363,7 @@ function useHouseRoster() {
|
|
|
363
363
|
return Boolean(process.env.OZ_HIJACK_POD) && !sniffOn();
|
|
364
364
|
}
|
|
365
365
|
function loadAgents() {
|
|
366
|
-
const house = readHouseRoster(HOME, activeAccountId) || [];
|
|
366
|
+
const house = filterDeleted(readHouseRoster(HOME, activeAccountId) || [], HOME);
|
|
367
367
|
const shaped = house.map(shapeAgent);
|
|
368
368
|
const dirty = shaped.some((a, i) => a.brief && a.brief !== String(house[i]?.brief || house[i]?.description || ''));
|
|
369
369
|
if (dirty && shaped.length) {
|
|
@@ -1264,6 +1264,10 @@ async function fireAgentWakeup(agentId) {
|
|
|
1264
1264
|
wakeupLog(`cursor-backend: wakeup skip busy agent=${id}`);
|
|
1265
1265
|
return;
|
|
1266
1266
|
}
|
|
1267
|
+
if (focusedAgentId && String(focusedAgentId) === id) {
|
|
1268
|
+
wakeupLog(`cursor-backend: wakeup skip focused agent=${id}`);
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1267
1271
|
const nonce = `oz-wakeup-${id}-${rec.lastAt}`;
|
|
1268
1272
|
const prompt = rec.prompt || DEFAULT_WAKEUP_PROMPT;
|
|
1269
1273
|
const turn = zooTurns.begin(id, nonce);
|
|
@@ -1299,7 +1303,9 @@ export function restoreAgentWakeups(log = () => {}) {
|
|
|
1299
1303
|
const ids = Object.keys(map);
|
|
1300
1304
|
const now = Date.now();
|
|
1301
1305
|
ids.forEach((id, i) => {
|
|
1302
|
-
|
|
1306
|
+
// Do not fire during Grok Bot boot. 10 parallel zoo turns + CDP on the
|
|
1307
|
+
// spinner page left the window on a white disc. First tick is +90s.
|
|
1308
|
+
map[id].nextAt = now + 90_000 + i * 4000;
|
|
1303
1309
|
});
|
|
1304
1310
|
if (ids.length) persistWakeups(map);
|
|
1305
1311
|
for (const id of ids) armWakeup(map[id]);
|
|
@@ -1534,7 +1540,7 @@ function mergeAgentLists(remote) {
|
|
|
1534
1540
|
seen.add(a.id);
|
|
1535
1541
|
out.push(stampActivity(a));
|
|
1536
1542
|
}
|
|
1537
|
-
return sortAgentsByActivity(out);
|
|
1543
|
+
return sortAgentsByActivity(filterDeleted(out, HOME));
|
|
1538
1544
|
}
|
|
1539
1545
|
function gatewayEntry(e) {
|
|
1540
1546
|
const { seq, pulledRemote, promptRaw, ephemeral, ...rest } = e;
|
|
@@ -2906,8 +2912,12 @@ async function handleHijackedPodHttp(req, res, full, body, log) {
|
|
|
2906
2912
|
try { parsed = JSON.parse(String(body || '{}')); } catch { parsed = {}; }
|
|
2907
2913
|
const ids = new Set([].concat(parsed.ids || parsed.id || []).map(String));
|
|
2908
2914
|
const next = (cachedAgentList() || []).filter((a) => !ids.has(a.id));
|
|
2915
|
+
addDeletedIds(HOME, [...ids]);
|
|
2909
2916
|
saveAgents(next);
|
|
2910
|
-
for (const id of ids)
|
|
2917
|
+
for (const id of ids) {
|
|
2918
|
+
transcripts.delete(id);
|
|
2919
|
+
try { cancelAgentWakeup(id); } catch { /* */ }
|
|
2920
|
+
}
|
|
2911
2921
|
jsonSend(res, { ok: true, deleted: [...ids] });
|
|
2912
2922
|
log(`cursor-backend: deleteAgents n=${ids.size}`);
|
|
2913
2923
|
return true;
|
package/lib/grokbotAccount.js
CHANGED
|
@@ -244,6 +244,31 @@ export function readWakeups(home) {
|
|
|
244
244
|
return out;
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
+
export function deletedAgentsPath(home) {
|
|
248
|
+
return path.join(home, '.openzoo', 'grokbot-deleted.json');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export function readDeletedIds(home) {
|
|
252
|
+
const raw = readJsonFile(deletedAgentsPath(home));
|
|
253
|
+
const ids = Array.isArray(raw) ? raw : (raw && Array.isArray(raw.ids) ? raw.ids : []);
|
|
254
|
+
return new Set(ids.map(String).filter(Boolean));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function addDeletedIds(home, ids) {
|
|
258
|
+
const s = readDeletedIds(home);
|
|
259
|
+
for (const id of ids) if (id) s.add(String(id));
|
|
260
|
+
const dir = path.dirname(deletedAgentsPath(home));
|
|
261
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
262
|
+
fs.writeFileSync(deletedAgentsPath(home), JSON.stringify([...s], null, 2));
|
|
263
|
+
return s;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export function filterDeleted(agents, home) {
|
|
267
|
+
const del = readDeletedIds(home);
|
|
268
|
+
if (!del.size || !Array.isArray(agents)) return agents || [];
|
|
269
|
+
return agents.filter((a) => a && !del.has(String(a.id)));
|
|
270
|
+
}
|
|
271
|
+
|
|
247
272
|
export function writeWakeups(home, map) {
|
|
248
273
|
const dir = path.dirname(wakeupsPath(home));
|
|
249
274
|
fs.mkdirSync(dir, { recursive: true });
|
package/lib/grokcli.js
CHANGED
|
@@ -110,7 +110,7 @@ const GROK_BOT_BIN = `${APP}/Contents/MacOS/Grok Bot`;
|
|
|
110
110
|
/** pids whose command is the Grok Bot main binary (not grep itself). */
|
|
111
111
|
export function grokBotPids(run = execSync) {
|
|
112
112
|
try {
|
|
113
|
-
return run(`pgrep -f ${JSON.stringify(GROK_BOT_BIN)}`, { encoding: 'utf8' })
|
|
113
|
+
return run(`pgrep -f ${JSON.stringify(GROK_BOT_BIN)}`, { encoding: 'utf8', shell: true })
|
|
114
114
|
.trim().split('\n').map((s) => s.trim()).filter(Boolean);
|
|
115
115
|
} catch {
|
|
116
116
|
return [];
|
|
@@ -296,7 +296,7 @@ export async function runBot(argv = []) {
|
|
|
296
296
|
await new Promise((r) => setTimeout(r, 1500));
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
-
|
|
299
|
+
const launchGrokBot = () => {
|
|
300
300
|
console.error('openzoo: launching Grok Bot');
|
|
301
301
|
console.error(` CURSOR_API_BASE_URL=${url}`);
|
|
302
302
|
spawn(bin, grokBotChromiumArgs(), {
|
|
@@ -315,14 +315,14 @@ export async function runBot(argv = []) {
|
|
|
315
315
|
SAND_HOST_GATEWAY_NETWORK_TOKEN: 'openzoo',
|
|
316
316
|
},
|
|
317
317
|
}).unref();
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
|
|
318
|
+
};
|
|
319
|
+
if (plan.spawn) launchGrokBot();
|
|
320
|
+
else console.error('openzoo: Grok Bot already hijacked — not spawning another copy');
|
|
321
321
|
|
|
322
322
|
injectSpendChipInBackground({
|
|
323
323
|
port: GROKBOT_CDP_PORT,
|
|
324
324
|
log: (m) => console.error(m),
|
|
325
|
-
delayMs: plan.spawn ?
|
|
325
|
+
delayMs: plan.spawn ? 8000 : 2000,
|
|
326
326
|
});
|
|
327
327
|
|
|
328
328
|
console.error('openzoo: leave this running. ctrl-c stops the backend.');
|
package/lib/ozSpendChip.js
CHANGED
|
@@ -7,9 +7,27 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import net from 'node:net';
|
|
9
9
|
import crypto from 'node:crypto';
|
|
10
|
+
import fs from 'node:fs';
|
|
11
|
+
import os from 'node:os';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { spendChipLabel } from './spendProof.js';
|
|
10
14
|
|
|
11
15
|
export const GROKBOT_CDP_PORT = Number(process.env.OZ_GROKBOT_CDP_PORT || 9444);
|
|
12
16
|
|
|
17
|
+
/** Session totals for a floating pill when the open canvas has no footer. */
|
|
18
|
+
export function sessionSpendLabel(home = os.homedir()) {
|
|
19
|
+
try {
|
|
20
|
+
const s = JSON.parse(fs.readFileSync(path.join(home, '.openzoo', 'session.json'), 'utf8'));
|
|
21
|
+
const spent = Number(s.spentUsd || s.spendUsd || 0);
|
|
22
|
+
const would = Number(s.directUsd || 0);
|
|
23
|
+
const saved = Number(s.savedUsd != null ? s.savedUsd : Math.max(0, would - spent));
|
|
24
|
+
if (!(spent > 0.00005)) return '';
|
|
25
|
+
return spendChipLabel({ spent, would, saved, pct: would > 0 ? (100 * saved) / would : 0 });
|
|
26
|
+
} catch {
|
|
27
|
+
return '';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
13
31
|
export function grokBotChromiumArgs(port = GROKBOT_CDP_PORT) {
|
|
14
32
|
return [
|
|
15
33
|
'--ignore-certificate-errors',
|
|
@@ -133,9 +151,12 @@ export function spendOnlyText(t) {
|
|
|
133
151
|
}
|
|
134
152
|
|
|
135
153
|
function ozEnsureSpendCss() {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
154
|
+
let s = document.getElementById('oz-spend-css');
|
|
155
|
+
if (!s) {
|
|
156
|
+
s = document.createElement('style');
|
|
157
|
+
s.id = 'oz-spend-css';
|
|
158
|
+
(document.head || document.documentElement).appendChild(s);
|
|
159
|
+
}
|
|
139
160
|
s.textContent = [
|
|
140
161
|
'.oz-spend{margin:.55rem 0 0;font-size:12px;color:inherit;opacity:.82;max-width:36em}',
|
|
141
162
|
'.oz-spend>summary{cursor:help;list-style:none;display:inline-flex;align-items:center;gap:.35rem;',
|
|
@@ -144,8 +165,9 @@ function ozEnsureSpendCss() {
|
|
|
144
165
|
'.oz-spend>summary::-webkit-details-marker{display:none}',
|
|
145
166
|
'.oz-spend-body{white-space:pre-wrap;margin:.55rem 0 0;font-size:11px;line-height:1.45;opacity:.88;overflow-wrap:anywhere}',
|
|
146
167
|
'[data-oz-spend-hide]{display:none!important}',
|
|
168
|
+
'button[aria-label="Start voice input"],button[aria-label*="voice input" i],',
|
|
169
|
+
'button[aria-label*="steminvoer" i],button[aria-label^="Microphone"]{display:none!important}',
|
|
147
170
|
].join('');
|
|
148
|
-
(document.head || document.documentElement).appendChild(s);
|
|
149
171
|
}
|
|
150
172
|
|
|
151
173
|
function ozSpendHost(el) {
|
|
@@ -292,27 +314,91 @@ function ozCollapseSpend() {
|
|
|
292
314
|
ozAttachSpendChip(host, split);
|
|
293
315
|
}
|
|
294
316
|
ozHideSpendLeftovers();
|
|
317
|
+
ozEnsureFloatSpend();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function ozEnsureFloatSpend() {
|
|
321
|
+
const label = String(window.__OZ_SESSION_SPEND__ || '').trim();
|
|
322
|
+
const msgPills = document.querySelectorAll('.oz-spend:not(#oz-spend-float)').length;
|
|
323
|
+
let el = document.getElementById('oz-spend-float');
|
|
324
|
+
if (!label || msgPills) {
|
|
325
|
+
if (el) el.remove();
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
ozEnsureSpendCss();
|
|
329
|
+
if (!el) {
|
|
330
|
+
el = document.createElement('details');
|
|
331
|
+
el.id = 'oz-spend-float';
|
|
332
|
+
el.className = 'oz-spend';
|
|
333
|
+
const sum = document.createElement('summary');
|
|
334
|
+
const body = document.createElement('div');
|
|
335
|
+
body.className = 'oz-spend-body';
|
|
336
|
+
body.textContent = 'session spend (this openzoo bot)';
|
|
337
|
+
el.appendChild(sum);
|
|
338
|
+
el.appendChild(body);
|
|
339
|
+
document.body.appendChild(el);
|
|
340
|
+
}
|
|
341
|
+
ozPlaceFloat(el);
|
|
342
|
+
const sum = el.querySelector('summary');
|
|
343
|
+
if (sum) sum.textContent = 'ⓘ ' + label;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function ozPlaceFloat(el) {
|
|
347
|
+
if (!el) return;
|
|
348
|
+
const ta = document.querySelector('textarea, [contenteditable="true"]');
|
|
349
|
+
const box = ta && ta.getBoundingClientRect();
|
|
350
|
+
el.style.position = 'fixed';
|
|
351
|
+
el.style.right = 'auto';
|
|
352
|
+
el.style.zIndex = '2147483646';
|
|
353
|
+
el.style.opacity = '0.95';
|
|
354
|
+
if (box && box.width > 80 && box.top > 48) {
|
|
355
|
+
el.style.left = Math.round(box.left) + 'px';
|
|
356
|
+
el.style.top = Math.round(Math.max(8, box.top - 56)) + 'px';
|
|
357
|
+
el.style.bottom = 'auto';
|
|
358
|
+
} else {
|
|
359
|
+
el.style.left = '96px';
|
|
360
|
+
el.style.bottom = '72px';
|
|
361
|
+
el.style.top = 'auto';
|
|
362
|
+
}
|
|
295
363
|
}
|
|
296
364
|
|
|
297
365
|
function ozWatchSpend() {
|
|
298
|
-
|
|
366
|
+
let t = 0;
|
|
367
|
+
const run = () => {
|
|
368
|
+
const a = document.activeElement;
|
|
369
|
+
if (a && (a.tagName === 'TEXTAREA' || a.tagName === 'INPUT' || a.isContentEditable)) return;
|
|
370
|
+
try { ozCollapseSpend(); } catch (e) {}
|
|
371
|
+
};
|
|
372
|
+
const debounced = () => { clearTimeout(t); t = setTimeout(run, 600); };
|
|
299
373
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', run);
|
|
300
374
|
else run();
|
|
301
375
|
try {
|
|
302
|
-
const mo = new MutationObserver(
|
|
303
|
-
const start = () => { if (document.body) mo.observe(document.body, { childList: true, subtree:
|
|
376
|
+
const mo = new MutationObserver(debounced);
|
|
377
|
+
const start = () => { if (document.body) mo.observe(document.body, { childList: true, subtree: false }); };
|
|
304
378
|
if (document.body) start();
|
|
305
379
|
else document.addEventListener('DOMContentLoaded', start);
|
|
306
|
-
} catch {
|
|
307
|
-
|
|
380
|
+
} catch (e) {}
|
|
381
|
+
try { window.addEventListener('resize', debounced); } catch (e) {}
|
|
308
382
|
}
|
|
309
383
|
|
|
310
384
|
export function spendChipSource() {
|
|
311
385
|
return [
|
|
312
386
|
'(function ozSpendChip(){',
|
|
313
387
|
"'use strict';",
|
|
314
|
-
'if (window.__OZ_SPEND_CHIP__ ===
|
|
315
|
-
'
|
|
388
|
+
'if (window.__OZ_SPEND_CHIP__ === 11) {',
|
|
389
|
+
' const el = document.getElementById("oz-spend-float");',
|
|
390
|
+
' const ta = document.querySelector("textarea, [contenteditable=\\"true\\"]");',
|
|
391
|
+
' const box = ta && ta.getBoundingClientRect();',
|
|
392
|
+
' if (el && box && box.width > 80) {',
|
|
393
|
+
' el.style.position = "fixed";',
|
|
394
|
+
' el.style.left = Math.round(box.left) + "px";',
|
|
395
|
+
' el.style.top = Math.round(Math.max(8, box.top - 56)) + "px";',
|
|
396
|
+
' el.style.right = "auto";',
|
|
397
|
+
' el.style.bottom = "auto";',
|
|
398
|
+
' }',
|
|
399
|
+
' return;',
|
|
400
|
+
'}',
|
|
401
|
+
'window.__OZ_SPEND_CHIP__ = 11;',
|
|
316
402
|
chipUsd.toString(),
|
|
317
403
|
labelFromSpendBody.toString(),
|
|
318
404
|
spendLinesOnly.toString(),
|
|
@@ -327,6 +413,8 @@ export function spendChipSource() {
|
|
|
327
413
|
ozPreviousMessageCard.toString(),
|
|
328
414
|
ozAttachSpendChip.toString(),
|
|
329
415
|
ozCollapseSpend.toString(),
|
|
416
|
+
ozEnsureFloatSpend.toString(),
|
|
417
|
+
ozPlaceFloat.toString(),
|
|
330
418
|
ozWatchSpend.toString(),
|
|
331
419
|
'ozWatchSpend();',
|
|
332
420
|
'})();',
|
|
@@ -511,12 +599,24 @@ export async function listCdpPages(port, fetchImpl = fetch) {
|
|
|
511
599
|
return [];
|
|
512
600
|
}
|
|
513
601
|
|
|
514
|
-
async function injectTarget(target, source, connect) {
|
|
602
|
+
async function injectTarget(target, source, connect, { waitForUi = false } = {}) {
|
|
515
603
|
const session = await connect(target.webSocketDebuggerUrl);
|
|
516
604
|
try {
|
|
517
605
|
await session.send('Page.enable').catch(() => {});
|
|
518
606
|
await session.send('Runtime.enable').catch(() => {});
|
|
519
607
|
await session.send('Page.addScriptToEvaluateOnNewDocument', { source }).catch(() => {});
|
|
608
|
+
if (waitForUi) {
|
|
609
|
+
for (let i = 0; i < 32; i += 1) {
|
|
610
|
+
try {
|
|
611
|
+
const r = await session.send('Runtime.evaluate', {
|
|
612
|
+
expression: '!!document.querySelector(\'[class*="sand-"], textarea, [contenteditable="true"]\')',
|
|
613
|
+
returnByValue: true,
|
|
614
|
+
});
|
|
615
|
+
if (r?.result?.value) break;
|
|
616
|
+
} catch { /* still booting */ }
|
|
617
|
+
await new Promise((ok) => setTimeout(ok, 250));
|
|
618
|
+
}
|
|
619
|
+
}
|
|
520
620
|
await session.send('Runtime.evaluate', { expression: source, awaitPromise: false });
|
|
521
621
|
} finally {
|
|
522
622
|
session.close();
|
|
@@ -531,6 +631,7 @@ export async function injectSpendChip({
|
|
|
531
631
|
connect = cdpSession,
|
|
532
632
|
tries = 40,
|
|
533
633
|
delayMs = 400,
|
|
634
|
+
waitForUi = false,
|
|
534
635
|
} = {}) {
|
|
535
636
|
let targets = [];
|
|
536
637
|
for (let i = 0; i < Math.max(1, tries); i += 1) {
|
|
@@ -542,10 +643,12 @@ export async function injectSpendChip({
|
|
|
542
643
|
log(`openzoo: spend chip CDP :${port} has no pages yet`);
|
|
543
644
|
return { ok: false, injected: 0 };
|
|
544
645
|
}
|
|
646
|
+
const label = sessionSpendLabel();
|
|
647
|
+
const src = `window.__OZ_SESSION_SPEND__=${JSON.stringify(label)};\n${source}`;
|
|
545
648
|
let injected = 0;
|
|
546
649
|
for (const t of targets) {
|
|
547
650
|
try {
|
|
548
|
-
await injectTarget(t,
|
|
651
|
+
await injectTarget(t, src, connect, { waitForUi });
|
|
549
652
|
injected += 1;
|
|
550
653
|
} catch (e) {
|
|
551
654
|
log(`openzoo: spend chip inject ${e.message}`);
|
|
@@ -555,11 +658,23 @@ export async function injectSpendChip({
|
|
|
555
658
|
return { ok: injected > 0, injected };
|
|
556
659
|
}
|
|
557
660
|
|
|
558
|
-
/**
|
|
661
|
+
/** One CDP attach, then drop the debugger. A loop here pauses the renderer
|
|
662
|
+
* so the composer accepts a keystroke and then dies. Reloads pick the chip
|
|
663
|
+
* up via Page.addScriptToEvaluateOnNewDocument from that single inject. */
|
|
559
664
|
export function injectSpendChipInBackground(opts = {}) {
|
|
560
665
|
const log = opts.log || ((m) => console.error(m));
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
666
|
+
const wait = Math.max(0, Number(opts.delayMs) || 400);
|
|
667
|
+
setTimeout(() => {
|
|
668
|
+
injectSpendChip({
|
|
669
|
+
tries: 24,
|
|
670
|
+
delayMs: 400,
|
|
671
|
+
waitForUi: true,
|
|
672
|
+
...opts,
|
|
673
|
+
log,
|
|
674
|
+
connect: (ws) => cdpSession(ws, { timeoutMs: 2500 }),
|
|
675
|
+
}).catch((e) => {
|
|
676
|
+
log(`openzoo: spend chip inject failed ${e.message}`);
|
|
677
|
+
});
|
|
678
|
+
}, wait);
|
|
564
679
|
}
|
|
565
680
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.50.
|
|
3
|
+
"version": "0.50.51",
|
|
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",
|