openzoo 0.50.34 → 0.50.35
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/bin/openzoo.js +4 -0
- package/lib/grokcli.js +104 -21
- package/package.json +1 -1
package/bin/openzoo.js
CHANGED
|
@@ -93,6 +93,10 @@ usage:
|
|
|
93
93
|
:8443, then launches the app with
|
|
94
94
|
CURSOR_API_BASE_URL=https://127.0.0.1:8443
|
|
95
95
|
(no sudo, no /etc/hosts). Leave it running.
|
|
96
|
+
After a reboot the login item comes back without
|
|
97
|
+
that env — bot bounces it so send works. Already-
|
|
98
|
+
hijacked sessions are left alone.
|
|
99
|
+
--quit force bounce · --no-quit never bounce
|
|
96
100
|
npx openzoo cursor [dir] start proxy+tunnel, write MCP config + every model
|
|
97
101
|
into the picker, and LAUNCH Cursor on [dir]
|
|
98
102
|
(defaults to the current directory; ~ works;
|
package/lib/grokcli.js
CHANGED
|
@@ -99,6 +99,74 @@ ${body}
|
|
|
99
99
|
`;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
const GROK_BOT_BIN = `${APP}/Contents/MacOS/Grok Bot`;
|
|
103
|
+
|
|
104
|
+
/** pids whose command is the Grok Bot main binary (not grep itself). */
|
|
105
|
+
export function grokBotPids(run = execSync) {
|
|
106
|
+
try {
|
|
107
|
+
return run(`pgrep -f ${JSON.stringify(GROK_BOT_BIN)}`, { encoding: 'utf8' })
|
|
108
|
+
.trim().split('\n').map((s) => s.trim()).filter(Boolean);
|
|
109
|
+
} catch {
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function pidHasNeedle(pid, needle, run = execSync) {
|
|
115
|
+
const n = Number(pid);
|
|
116
|
+
if (!Number.isInteger(n) || n <= 0) return false;
|
|
117
|
+
for (const cmd of [`ps -Eww -p ${n} -o command=`, `ps eww -p ${n}`]) {
|
|
118
|
+
try {
|
|
119
|
+
if (run(cmd, { encoding: 'utf8', timeout: 2000 }).includes(needle)) return true;
|
|
120
|
+
} catch { /* SIP / dead pid */ }
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function somethingTalksToPort(port, run = execSync) {
|
|
126
|
+
try {
|
|
127
|
+
const out = run(`lsof -nP -iTCP:${Number(port)} -sTCP:ESTABLISHED`, {
|
|
128
|
+
encoding: 'utf8', timeout: 2000,
|
|
129
|
+
});
|
|
130
|
+
return /Grok|Electron|Helper/i.test(out);
|
|
131
|
+
} catch {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Is the running Grok Bot already pointed at our aiserver? */
|
|
137
|
+
export function inspectGrokBotHijack(url, port = 8443, run = execSync) {
|
|
138
|
+
const pids = grokBotPids(run);
|
|
139
|
+
const needle = `CURSOR_API_BASE_URL=${url}`;
|
|
140
|
+
const envHit = pids.some((pid) => pidHasNeedle(pid, needle, run));
|
|
141
|
+
const tcpHit = somethingTalksToPort(port, run);
|
|
142
|
+
return { running: pids.length > 0, pids, hijacked: envHit || tcpHit };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Decide whether to osascript-quit / spawn Grok Bot.
|
|
147
|
+
* forceQuit = --quit / OZ_NO_QUIT=0
|
|
148
|
+
* neverQuit = --no-quit / OZ_NO_QUIT=1
|
|
149
|
+
*/
|
|
150
|
+
export function grokBotQuitPlan({ forceQuit = false, neverQuit = false, running = false, hijacked = false } = {}) {
|
|
151
|
+
if (forceQuit) return { quit: true, spawn: true, reason: 'forced --quit' };
|
|
152
|
+
if (neverQuit) {
|
|
153
|
+
return {
|
|
154
|
+
quit: false,
|
|
155
|
+
spawn: !hijacked,
|
|
156
|
+
reason: hijacked
|
|
157
|
+
? 'explicit --no-quit (already hijacked)'
|
|
158
|
+
: 'explicit --no-quit (Grok Bot may be a login-item without hijack env)',
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
if (!running) return { quit: false, spawn: true, reason: 'Grok Bot not running' };
|
|
162
|
+
if (hijacked) return { quit: false, spawn: false, reason: 'already hijacked — leaving the session' };
|
|
163
|
+
return {
|
|
164
|
+
quit: true,
|
|
165
|
+
spawn: true,
|
|
166
|
+
reason: 'running without hijack env (reboot/login item) — bouncing so CURSOR_API_BASE_URL sticks',
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
102
170
|
/**
|
|
103
171
|
* `openzoo bot` — Grok Bot.app on the zoo, no sudo, no /etc/hosts.
|
|
104
172
|
*
|
|
@@ -185,31 +253,46 @@ export async function runBot(argv = []) {
|
|
|
185
253
|
console.error(' EnsureSandBox HIJACKED here; StreamUnifiedChat -> :8402 (x402)');
|
|
186
254
|
}
|
|
187
255
|
|
|
188
|
-
// Default:
|
|
189
|
-
//
|
|
190
|
-
|
|
256
|
+
// Default: leave a Grok Bot that is ALREADY hijacked. After a reboot the
|
|
257
|
+
// login item comes back WITHOUT CURSOR_API_BASE_URL; Electron's single-
|
|
258
|
+
// instance lock then swallows our env'd spawn and the overlay stays
|
|
259
|
+
// "Couldn't send your message". Bounce only that unhijacked case.
|
|
260
|
+
// --quit / OZ_NO_QUIT=0 force a bounce. --no-quit / OZ_NO_QUIT=1 never.
|
|
261
|
+
const state = inspectGrokBotHijack(url, port);
|
|
262
|
+
const plan = grokBotQuitPlan({
|
|
263
|
+
forceQuit: argv.includes('--quit') || process.env.OZ_NO_QUIT === '0',
|
|
264
|
+
neverQuit: argv.includes('--no-quit') || process.env.OZ_NO_QUIT === '1',
|
|
265
|
+
running: state.running,
|
|
266
|
+
hijacked: state.hijacked,
|
|
267
|
+
});
|
|
268
|
+
console.error(`openzoo: grokbot ${plan.reason}`);
|
|
269
|
+
if (plan.quit) {
|
|
191
270
|
try { execSync('osascript -e \'tell application "Grok Bot" to quit\'', { stdio: 'ignore' }); } catch { /* ok */ }
|
|
192
271
|
await new Promise((r) => setTimeout(r, 1500));
|
|
193
272
|
}
|
|
194
273
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
274
|
+
if (plan.spawn) {
|
|
275
|
+
console.error('openzoo: launching Grok Bot');
|
|
276
|
+
console.error(` CURSOR_API_BASE_URL=${url}`);
|
|
277
|
+
spawn(bin, ['--ignore-certificate-errors'], {
|
|
278
|
+
stdio: 'ignore',
|
|
279
|
+
detached: true,
|
|
280
|
+
env: {
|
|
281
|
+
...process.env,
|
|
282
|
+
NODE_TLS_REJECT_UNAUTHORIZED: '0',
|
|
283
|
+
CURSOR_API_BASE_URL: url,
|
|
284
|
+
SAND_BACKEND_URL: url,
|
|
285
|
+
// Helper daemon (local-exec-daemon/main.cjs) uses Node fetch, not Chromium.
|
|
286
|
+
// Without these it dials the cached cursorvm 1337 URL / dies on our
|
|
287
|
+
// self-signed cert and GET /local-exec/requests never arrives.
|
|
288
|
+
SAND_HOST_GATEWAY_URL: url,
|
|
289
|
+
SAND_HOST_GATEWAY_TOKEN: 'openzoo',
|
|
290
|
+
SAND_HOST_GATEWAY_NETWORK_TOKEN: 'openzoo',
|
|
291
|
+
},
|
|
292
|
+
}).unref();
|
|
293
|
+
} else {
|
|
294
|
+
console.error('openzoo: Grok Bot already hijacked — not spawning another copy');
|
|
295
|
+
}
|
|
213
296
|
|
|
214
297
|
console.error('openzoo: leave this running. ctrl-c stops the backend.');
|
|
215
298
|
if (argv.includes('--once')) return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.50.
|
|
3
|
+
"version": "0.50.35",
|
|
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",
|