openzoo 0.42.0 → 0.43.0
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/podagent.mjs +111 -1
- package/package.json +1 -1
package/lib/podagent.mjs
CHANGED
|
@@ -30,6 +30,109 @@ const PROXY = process.env.OZ_PROXY || 'http://127.0.0.1:8402/v1';
|
|
|
30
30
|
const MODEL = process.env.OZ_BRAIN_MODEL || 'x-ai/grok-4.6';
|
|
31
31
|
const MAX_STEPS = Number(process.env.OZ_MAX_STEPS || 10);
|
|
32
32
|
|
|
33
|
+
// Matches the Grok Bot chat surface itself (dark canvas, right-aligned grey
|
|
34
|
+
// user pills, pink-avatar left-aligned replies, pill input bar with + / mic) —
|
|
35
|
+
// this renders INSIDE Grok Bot's own sandbox panel (its sidebar/titlebar are
|
|
36
|
+
// the app's own chrome, not ours), so only the message canvas needs to match.
|
|
37
|
+
const VNC_CHAT_HTML = `<!doctype html>
|
|
38
|
+
<html><head><meta charset="utf-8"><title>openzoo box</title>
|
|
39
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
40
|
+
<style>
|
|
41
|
+
:root { color-scheme: dark; }
|
|
42
|
+
* { box-sizing: border-box; }
|
|
43
|
+
html, body { margin: 0; height: 100%; background: #000; }
|
|
44
|
+
body { color: #ececec; font: 15px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
|
45
|
+
display: flex; flex-direction: column; }
|
|
46
|
+
#log { flex: 1; overflow-y: auto; padding: 28px 24px 12px; display: flex; flex-direction: column; gap: 18px; }
|
|
47
|
+
.row { display: flex; align-items: flex-start; gap: 10px; max-width: 78%; }
|
|
48
|
+
.row.user { align-self: flex-end; flex-direction: row-reverse; }
|
|
49
|
+
.row.bot { align-self: flex-start; }
|
|
50
|
+
.avatar { width: 30px; height: 30px; border-radius: 8px; flex: 0 0 30px; background: #e91e8c;
|
|
51
|
+
display: flex; align-items: center; justify-content: center; }
|
|
52
|
+
.avatar svg { width: 16px; height: 16px; }
|
|
53
|
+
.bubble { padding: 11px 15px; border-radius: 18px; white-space: pre-wrap; word-break: break-word; }
|
|
54
|
+
.row.user .bubble { background: #3a3a3c; border-bottom-right-radius: 5px; }
|
|
55
|
+
.row.bot .bubble { background: transparent; padding: 6px 0; color: #ececec; }
|
|
56
|
+
.row.bot.pending .bubble { color: #8e8e93; }
|
|
57
|
+
.dots span { display: inline-block; width: 5px; height: 5px; margin-right: 3px; border-radius: 50%;
|
|
58
|
+
background: #8e8e93; animation: blink 1.2s infinite ease-in-out; }
|
|
59
|
+
.dots span:nth-child(2) { animation-delay: .2s; } .dots span:nth-child(3) { animation-delay: .4s; }
|
|
60
|
+
@keyframes blink { 0%, 80%, 100% { opacity: .25; } 40% { opacity: 1; } }
|
|
61
|
+
#bar { padding: 10px 16px 18px; }
|
|
62
|
+
#pill { display: flex; align-items: center; gap: 10px; background: #2c2c2e; border-radius: 26px;
|
|
63
|
+
padding: 8px 10px 8px 14px; }
|
|
64
|
+
.icon-btn { width: 32px; height: 32px; border-radius: 50%; border: none; background: transparent;
|
|
65
|
+
color: #ececec; display: flex; align-items: center; justify-content: center; cursor: pointer;
|
|
66
|
+
flex: 0 0 32px; }
|
|
67
|
+
.icon-btn:hover { background: #3a3a3c; }
|
|
68
|
+
.icon-btn svg { width: 18px; height: 18px; }
|
|
69
|
+
#inp { flex: 1; background: transparent; border: none; color: #ececec; font: inherit;
|
|
70
|
+
padding: 6px 0; min-width: 0; }
|
|
71
|
+
#inp::placeholder { color: #8e8e93; }
|
|
72
|
+
#inp:focus { outline: none; }
|
|
73
|
+
</style></head>
|
|
74
|
+
<body>
|
|
75
|
+
<div id="log"></div>
|
|
76
|
+
<div id="bar">
|
|
77
|
+
<div id="pill">
|
|
78
|
+
<button class="icon-btn" tabindex="-1">
|
|
79
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
|
80
|
+
</button>
|
|
81
|
+
<input id="inp" placeholder="Message pods" autofocus>
|
|
82
|
+
<button class="icon-btn" id="send">
|
|
83
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5"/><path d="M6 11l6-6 6 6"/></svg>
|
|
84
|
+
</button>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
<script>
|
|
88
|
+
const log = document.getElementById('log');
|
|
89
|
+
const inp = document.getElementById('inp');
|
|
90
|
+
const send = document.getElementById('send');
|
|
91
|
+
const AVATAR = '<svg viewBox="0 0 24 24" fill="none"><path d="M6 10c0-1 1-2 2-1l2 2 2-2c1-1 2 0 2 1v2c0 2-2 3-4 3s-4-1-4-3v-2z" fill="#fff"/></svg>';
|
|
92
|
+
function addRow(who, text) {
|
|
93
|
+
const row = document.createElement('div');
|
|
94
|
+
row.className = 'row ' + who + (text === null ? ' pending' : '');
|
|
95
|
+
if (who === 'bot') {
|
|
96
|
+
const av = document.createElement('div');
|
|
97
|
+
av.className = 'avatar';
|
|
98
|
+
av.innerHTML = AVATAR;
|
|
99
|
+
row.appendChild(av);
|
|
100
|
+
}
|
|
101
|
+
const bubble = document.createElement('div');
|
|
102
|
+
bubble.className = 'bubble';
|
|
103
|
+
bubble.innerHTML = text === null ? '<span class="dots"><span></span><span></span><span></span></span>' : '';
|
|
104
|
+
if (text !== null) bubble.textContent = text;
|
|
105
|
+
row.appendChild(bubble);
|
|
106
|
+
log.appendChild(row);
|
|
107
|
+
log.scrollTop = log.scrollHeight;
|
|
108
|
+
return { row, bubble };
|
|
109
|
+
}
|
|
110
|
+
async function submit() {
|
|
111
|
+
const task = inp.value.trim();
|
|
112
|
+
if (!task) return;
|
|
113
|
+
inp.value = '';
|
|
114
|
+
inp.disabled = true; send.disabled = true;
|
|
115
|
+
addRow('user', task);
|
|
116
|
+
const pending = addRow('bot', null);
|
|
117
|
+
try {
|
|
118
|
+
const r = await fetch('/drive', {
|
|
119
|
+
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
120
|
+
body: JSON.stringify({ task }),
|
|
121
|
+
});
|
|
122
|
+
const j = await r.json();
|
|
123
|
+
pending.row.classList.remove('pending');
|
|
124
|
+
pending.bubble.textContent = j.text || '(no response)';
|
|
125
|
+
} catch (e) {
|
|
126
|
+
pending.row.classList.remove('pending');
|
|
127
|
+
pending.bubble.textContent = 'error: ' + e.message;
|
|
128
|
+
}
|
|
129
|
+
inp.disabled = false; send.disabled = false; inp.focus();
|
|
130
|
+
}
|
|
131
|
+
send.addEventListener('click', submit);
|
|
132
|
+
inp.addEventListener('keydown', (e) => { if (e.key === 'Enter') submit(); });
|
|
133
|
+
</script>
|
|
134
|
+
</body></html>`;
|
|
135
|
+
|
|
33
136
|
function record(entry) {
|
|
34
137
|
try { appendFileSync(LOG, JSON.stringify(entry) + '\n'); } catch { /* best effort */ }
|
|
35
138
|
console.log(`[agent:${entry.port ?? '-'}] ${entry.method || entry.ev} ${entry.path || ''} ${entry.bodyBytes ?? ''}${entry.frames ? ' frames=' + entry.frames : ''}`);
|
|
@@ -205,9 +308,16 @@ for (const port of PORTS) {
|
|
|
205
308
|
}
|
|
206
309
|
}
|
|
207
310
|
|
|
311
|
+
// THE REAL TRIGGER. Grok Bot's own chat (StreamUnifiedChat) never reaches
|
|
312
|
+
// us — inference happens server-side inside whatever pod EnsureSandBox
|
|
313
|
+
// named, and since we hijacked that to a pod that doesn't exist on
|
|
314
|
+
// Cursor's side, the UI's chat box just sits silent forever. But the app
|
|
315
|
+
// ALSO opens this vnc.html URL — OUR box — inside its own sandbox panel.
|
|
316
|
+
// Serve a real chat page here instead of a stub: the user types inside
|
|
317
|
+
// Grok Bot's own window, it POSTs straight to /drive on this box.
|
|
208
318
|
if (req.url && req.url.includes('/vnc')) {
|
|
209
319
|
res.writeHead(200, { 'content-type': 'text/html' });
|
|
210
|
-
res.end(
|
|
320
|
+
res.end(VNC_CHAT_HTML);
|
|
211
321
|
} else {
|
|
212
322
|
res.writeHead(200, { 'content-type': 'application/json' });
|
|
213
323
|
res.end('{"ok":true}');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.43.0",
|
|
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",
|