openzoo 0.50.22 → 0.50.24

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.
Files changed (2) hide show
  1. package/lib/cursorbackend.js +100 -14
  2. package/package.json +1 -1
@@ -542,12 +542,13 @@ async function proxyPodHttp(req, res, full, body, log) {
542
542
  if (!realPod?.agent) return false;
543
543
  const agent = new URL(realPod.agent);
544
544
  const headers = copyReqHeaders(req, agent.host);
545
- if (realPod.accessToken && !headers.authorization && !headers.Authorization) {
546
- headers.authorization = `Bearer ${realPod.accessToken}`;
547
- }
548
- if (realPod.token && !headers['x-anyrun-network-token']) {
549
- headers['x-anyrun-network-token'] = realPod.token;
550
- }
545
+ // Incoming Authorization is Grok Bot oauth to api2. 1340 wants EnsureSandBox
546
+ // field 11. Leaving oauth in place 401s listAgents (measured: tails via
547
+ // podJson with field-11 worked, roster proxy with copied oauth 401ed).
548
+ delete headers.authorization;
549
+ delete headers.Authorization;
550
+ if (realPod.accessToken) headers.authorization = `Bearer ${realPod.accessToken}`;
551
+ if (realPod.token) headers['x-anyrun-network-token'] = realPod.token;
551
552
  if (!headers['x-sand-slim-avatars']) headers['x-sand-slim-avatars'] = '1';
552
553
  const path0 = (full || '').split('?')[0];
553
554
  const interesting = /sendPrompt|Transcript|listAgents|promptAcceptance|openAgentTail|createAgent/i.test(path0);
@@ -771,9 +772,33 @@ function expandUserPath(p) {
771
772
  const transcripts = new Map();
772
773
  const tailedAgents = new Set();
773
774
  let lastSendEchoId = `oz-${Date.now()}`;
775
+ const TX_FILE = path.join(os.homedir(), '.openzoo', 'grokbot-transcripts.json');
776
+ function loadTranscripts() {
777
+ try {
778
+ const o = JSON.parse(fs.readFileSync(TX_FILE, 'utf8'));
779
+ for (const [id, t] of Object.entries(o || {})) {
780
+ transcripts.set(id, {
781
+ seq: Number(t.seq) || (t.entries || []).length,
782
+ entries: Array.isArray(t.entries) ? t.entries : [],
783
+ pulledRemote: false,
784
+ });
785
+ }
786
+ } catch { /* first run */ }
787
+ }
788
+ function saveTranscripts() {
789
+ try {
790
+ const o = {};
791
+ for (const [id, t] of transcripts) {
792
+ o[id] = { seq: t.seq, entries: t.entries.slice(-200) };
793
+ }
794
+ fs.mkdirSync(path.dirname(TX_FILE), { recursive: true });
795
+ fs.writeFileSync(TX_FILE, JSON.stringify(o));
796
+ } catch { /* */ }
797
+ }
798
+ loadTranscripts();
774
799
  function agentTranscript(id) {
775
800
  let t = transcripts.get(id);
776
- if (!t) { t = { seq: 0, entries: [] }; transcripts.set(id, t); }
801
+ if (!t) { t = { seq: 0, entries: [], pulledRemote: false }; transcripts.set(id, t); }
777
802
  return t;
778
803
  }
779
804
  function appendLine(agentId, role, text, extra = {}) {
@@ -809,6 +834,7 @@ function appendLine(agentId, role, text, extra = {}) {
809
834
  };
810
835
  }
811
836
  t.entries.push(e);
837
+ saveTranscripts();
812
838
  return e;
813
839
  }
814
840
  function fanoutLine(primaryId, role, text, extra = {}) {
@@ -853,9 +879,63 @@ function mergeAgentLists(remote) {
853
879
  return out;
854
880
  }
855
881
  function gatewayEntry(e) {
856
- const { seq, ...rest } = e;
882
+ const { seq, pulledRemote, ...rest } = e;
857
883
  return rest;
858
884
  }
885
+ async function podJson(path0, bodyObj, log) {
886
+ if (!realPod?.agent) return null;
887
+ let agent;
888
+ try { agent = new URL(realPod.agent); } catch { return null; }
889
+ const headers = {
890
+ 'content-type': 'application/json',
891
+ accept: 'application/json',
892
+ authorization: `Bearer ${realPod.accessToken || realPod.token || ''}`,
893
+ 'x-anyrun-network-token': realPod.token || '',
894
+ 'x-sand-slim-avatars': '1',
895
+ };
896
+ try {
897
+ const cap = await upstreamUnary({
898
+ host: agent.hostname,
899
+ path: path0,
900
+ method: 'POST',
901
+ headers,
902
+ body: Buffer.from(JSON.stringify(bodyObj || {})),
903
+ timeoutMs: 20000,
904
+ });
905
+ if (cap.status !== 200) {
906
+ log(`cursor-backend: podJson ${path0} ${cap.status}`);
907
+ return null;
908
+ }
909
+ const raw = inflateBody(cap.buf, cap.respHeaders);
910
+ return JSON.parse(String(raw));
911
+ } catch (e) {
912
+ log(`cursor-backend: podJson ${path0} ${e.message}`);
913
+ return null;
914
+ }
915
+ }
916
+ function ingestRemoteEntries(agentId, entries) {
917
+ if (!Array.isArray(entries) || !entries.length) return 0;
918
+ const t = agentTranscript(agentId);
919
+ const have = new Set(t.entries.map((e) => e.id || e.clientNonce || '').filter(Boolean));
920
+ const incoming = [];
921
+ for (const e of entries) {
922
+ if (!e || typeof e !== 'object') continue;
923
+ const k = e.id || e.clientNonce || '';
924
+ if (k && have.has(k)) continue;
925
+ incoming.push(e);
926
+ }
927
+ if (!incoming.length) return 0;
928
+ const local = t.entries;
929
+ t.entries = [];
930
+ t.seq = 0;
931
+ for (const e of [...incoming, ...local]) {
932
+ t.seq += 1;
933
+ const { seq: _s, ...rest } = e;
934
+ t.entries.push({ ...rest, seq: t.seq });
935
+ }
936
+ saveTranscripts();
937
+ return incoming.length;
938
+ }
859
939
 
860
940
  function promptFromSendBody(raw) {
861
941
  let obj = raw;
@@ -1419,16 +1499,22 @@ async function handleHijackedPodHttp(req, res, full, body, log) {
1419
1499
  const id = String(parsed.id || parsed.agentId || 'openzoo');
1420
1500
  tailedAgents.add(id);
1421
1501
  const t = agentTranscript(id);
1502
+ if (!t.pulledRemote && realPod?.agent) {
1503
+ const remote = await podJson('/api/getAgentTranscriptTail', {
1504
+ id, agentId: id, limit: 200, beforeSeq: parsed.beforeSeq,
1505
+ }, log);
1506
+ const n = ingestRemoteEntries(id, remote?.entries);
1507
+ t.pulledRemote = true;
1508
+ if (n) log(`cursor-backend: hydrated ${id} +${n} from 1340`);
1509
+ }
1510
+ const t2 = agentTranscript(id);
1422
1511
  const limit = Math.min(Number(parsed.limit) || 50, 200);
1423
1512
  const before = parsed.beforeSeq != null ? Number(parsed.beforeSeq) : Infinity;
1424
- const sliced = t.entries.filter((e) => e.seq < before).slice(-limit);
1513
+ const sliced = t2.entries.filter((e) => e.seq < before).slice(-limit);
1425
1514
  const page = { entries: sliced.map(gatewayEntry) };
1426
- if (t.entries.length > sliced.length && sliced.length) page.nextBeforeSeq = sliced[0].seq;
1427
- // Live pod returns RAW {entries, nextBeforeSeq} — no CVr envelope
1428
- // (measured 1340 getAgentTranscriptTail 2026-08-29). Wrapping {status,value}
1429
- // made transcript-page validation fail and the canvas stayed empty.
1515
+ if (t2.entries.length > sliced.length && sliced.length) page.nextBeforeSeq = sliced[0].seq;
1430
1516
  jsonSend(res, page);
1431
- log(`cursor-backend: -> transcript ${id} n=${sliced.length}/${t.seq}`);
1517
+ log(`cursor-backend: -> transcript ${id} n=${sliced.length}/${t2.seq}`);
1432
1518
  return true;
1433
1519
  }
1434
1520
  if (name === 'promptAcceptanceStatus') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.50.22",
3
+ "version": "0.50.24",
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",