openzoo 0.50.49 → 0.50.50

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.
@@ -1299,7 +1299,9 @@ export function restoreAgentWakeups(log = () => {}) {
1299
1299
  const ids = Object.keys(map);
1300
1300
  const now = Date.now();
1301
1301
  ids.forEach((id, i) => {
1302
- map[id].nextAt = now + 5000 + i * 2000;
1302
+ // Do not fire during Grok Bot boot. 10 parallel zoo turns + CDP on the
1303
+ // spinner page left the window on a white disc. First tick is +90s.
1304
+ map[id].nextAt = now + 90_000 + i * 4000;
1303
1305
  });
1304
1306
  if (ids.length) persistWakeups(map);
1305
1307
  for (const id of ids) armWakeup(map[id]);
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
- if (plan.spawn) {
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
- } else {
319
- console.error('openzoo: Grok Bot already hijacked — not spawning another copy');
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 ? 500 : 200,
325
+ delayMs: plan.spawn ? 8000 : 2000,
326
326
  });
327
327
 
328
328
  console.error('openzoo: leave this running. ctrl-c stops the backend.');
@@ -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
- if (document.getElementById('oz-spend-css')) return;
137
- const s = document.createElement('style');
138
- s.id = 'oz-spend-css';
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,6 +314,52 @@ 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() {
@@ -305,14 +373,27 @@ function ozWatchSpend() {
305
373
  else document.addEventListener('DOMContentLoaded', start);
306
374
  } catch { /* */ }
307
375
  setInterval(run, 1500);
376
+ try { window.addEventListener('resize', run); } catch (e) {}
308
377
  }
309
378
 
310
379
  export function spendChipSource() {
311
380
  return [
312
381
  '(function ozSpendChip(){',
313
382
  "'use strict';",
314
- 'if (window.__OZ_SPEND_CHIP__ === 4) return;',
315
- 'window.__OZ_SPEND_CHIP__ = 4;',
383
+ 'if (window.__OZ_SPEND_CHIP__ === 10) {',
384
+ ' const el = document.getElementById("oz-spend-float");',
385
+ ' const ta = document.querySelector("textarea, [contenteditable=\\"true\\"]");',
386
+ ' const box = ta && ta.getBoundingClientRect();',
387
+ ' if (el && box && box.width > 80) {',
388
+ ' el.style.position = "fixed";',
389
+ ' el.style.left = Math.round(box.left) + "px";',
390
+ ' el.style.top = Math.round(Math.max(8, box.top - 56)) + "px";',
391
+ ' el.style.right = "auto";',
392
+ ' el.style.bottom = "auto";',
393
+ ' }',
394
+ ' return;',
395
+ '}',
396
+ 'window.__OZ_SPEND_CHIP__ = 10;',
316
397
  chipUsd.toString(),
317
398
  labelFromSpendBody.toString(),
318
399
  spendLinesOnly.toString(),
@@ -327,6 +408,8 @@ export function spendChipSource() {
327
408
  ozPreviousMessageCard.toString(),
328
409
  ozAttachSpendChip.toString(),
329
410
  ozCollapseSpend.toString(),
411
+ ozEnsureFloatSpend.toString(),
412
+ ozPlaceFloat.toString(),
330
413
  ozWatchSpend.toString(),
331
414
  'ozWatchSpend();',
332
415
  '})();',
@@ -511,12 +594,24 @@ export async function listCdpPages(port, fetchImpl = fetch) {
511
594
  return [];
512
595
  }
513
596
 
514
- async function injectTarget(target, source, connect) {
597
+ async function injectTarget(target, source, connect, { waitForUi = false } = {}) {
515
598
  const session = await connect(target.webSocketDebuggerUrl);
516
599
  try {
517
600
  await session.send('Page.enable').catch(() => {});
518
601
  await session.send('Runtime.enable').catch(() => {});
519
602
  await session.send('Page.addScriptToEvaluateOnNewDocument', { source }).catch(() => {});
603
+ if (waitForUi) {
604
+ for (let i = 0; i < 32; i += 1) {
605
+ try {
606
+ const r = await session.send('Runtime.evaluate', {
607
+ expression: '!!document.querySelector(\'[class*="sand-"], textarea, [contenteditable="true"]\')',
608
+ returnByValue: true,
609
+ });
610
+ if (r?.result?.value) break;
611
+ } catch { /* still booting */ }
612
+ await new Promise((ok) => setTimeout(ok, 250));
613
+ }
614
+ }
520
615
  await session.send('Runtime.evaluate', { expression: source, awaitPromise: false });
521
616
  } finally {
522
617
  session.close();
@@ -531,6 +626,7 @@ export async function injectSpendChip({
531
626
  connect = cdpSession,
532
627
  tries = 40,
533
628
  delayMs = 400,
629
+ waitForUi = false,
534
630
  } = {}) {
535
631
  let targets = [];
536
632
  for (let i = 0; i < Math.max(1, tries); i += 1) {
@@ -542,10 +638,12 @@ export async function injectSpendChip({
542
638
  log(`openzoo: spend chip CDP :${port} has no pages yet`);
543
639
  return { ok: false, injected: 0 };
544
640
  }
641
+ const label = sessionSpendLabel();
642
+ const src = `window.__OZ_SESSION_SPEND__=${JSON.stringify(label)};\n${source}`;
545
643
  let injected = 0;
546
644
  for (const t of targets) {
547
645
  try {
548
- await injectTarget(t, source, connect);
646
+ await injectTarget(t, src, connect, { waitForUi });
549
647
  injected += 1;
550
648
  } catch (e) {
551
649
  log(`openzoo: spend chip inject ${e.message}`);
@@ -555,11 +653,23 @@ export async function injectSpendChip({
555
653
  return { ok: injected > 0, injected };
556
654
  }
557
655
 
558
- /** Fire-and-forget wrapper used by `openzoo bot`. */
656
+ /** One CDP attach, then drop the debugger. A loop here pauses the renderer
657
+ * so the composer accepts a keystroke and then dies. Reloads pick the chip
658
+ * up via Page.addScriptToEvaluateOnNewDocument from that single inject. */
559
659
  export function injectSpendChipInBackground(opts = {}) {
560
660
  const log = opts.log || ((m) => console.error(m));
561
- injectSpendChip({ ...opts, log }).catch((e) => {
562
- log(`openzoo: spend chip inject failed ${e.message}`);
563
- });
661
+ const wait = Math.max(0, Number(opts.delayMs) || 400);
662
+ setTimeout(() => {
663
+ injectSpendChip({
664
+ tries: 24,
665
+ delayMs: 400,
666
+ waitForUi: true,
667
+ ...opts,
668
+ log,
669
+ connect: (ws) => cdpSession(ws, { timeoutMs: 2500 }),
670
+ }).catch((e) => {
671
+ log(`openzoo: spend chip inject failed ${e.message}`);
672
+ });
673
+ }, wait);
564
674
  }
565
675
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.50.49",
3
+ "version": "0.50.50",
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",