openzoo 0.21.5 → 0.21.7
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/setup.js +14 -5
- package/lib/tunnel.js +20 -1
- package/package.json +1 -1
package/lib/setup.js
CHANGED
|
@@ -202,17 +202,26 @@ export async function setupEditor(which, target) {
|
|
|
202
202
|
// With --edge-ip-version 4 the tunnel comes up in seconds; if it somehow
|
|
203
203
|
// does not, we say so plainly and still launch.
|
|
204
204
|
if (!publicUrl) {
|
|
205
|
-
|
|
206
|
-
|
|
205
|
+
// VISIBLY ALIVE, AND SHORT. A silent 90s wait is indistinguishable from a
|
|
206
|
+
// hang — which is exactly how it was read, correctly. One dot per second
|
|
207
|
+
// with a countdown, capped at 25s, because with --edge-ip-version 4 a
|
|
208
|
+
// healthy tunnel registers in ~5-10s; longer than that means it is broken,
|
|
209
|
+
// not slow, and waiting more does not help.
|
|
210
|
+
const LIMIT = Number(process.env.OPENZOO_TUNNEL_WAIT_S || 25);
|
|
211
|
+
for (let i = 0; i < LIMIT * 2 && !started?.publicUrl; i++) {
|
|
207
212
|
await new Promise((r) => setTimeout(r, 500));
|
|
208
|
-
if (i %
|
|
213
|
+
if (i % 2 === 1) process.stdout.write(`\rwaiting for the public tunnel — ${LIMIT - Math.floor(i / 2) - 1}s `);
|
|
209
214
|
}
|
|
210
|
-
|
|
215
|
+
process.stdout.write('\r\x1b[K');
|
|
211
216
|
publicUrl = started?.publicUrl ?? null;
|
|
212
217
|
tunnelKey = started?.tunnelToken ?? tunnelKey;
|
|
213
218
|
}
|
|
214
219
|
if (publicUrl) console.log(`tunnel: ${publicUrl}/v1 api_key ${tunnelKey}`);
|
|
215
|
-
else
|
|
220
|
+
else {
|
|
221
|
+
console.log('tunnel: did NOT come up. cloudflared could not reach Cloudflare\'s edge.');
|
|
222
|
+
console.log(' most common cause here: no working IPv6 route (we already force');
|
|
223
|
+
console.log(' --edge-ip-version 4). check: npx openzoo tunnel for the raw log.');
|
|
224
|
+
}
|
|
216
225
|
} else {
|
|
217
226
|
console.log(`proxy already running on ${base}`);
|
|
218
227
|
// A proxy someone else started owns the tunnel; ask it for the public URL.
|
package/lib/tunnel.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* OPENZOO_MAX_USD_PER_CALL and/or OPENZOO_TUNNEL_MAX_USD to add them;
|
|
21
21
|
* - every tunnel-served request is logged with its running spend.
|
|
22
22
|
*/
|
|
23
|
-
import { spawn } from 'node:child_process';
|
|
23
|
+
import { spawn, execFileSync } from 'node:child_process';
|
|
24
24
|
import { createWriteStream } from 'node:fs';
|
|
25
25
|
import fs from 'node:fs';
|
|
26
26
|
import path from 'node:path';
|
|
@@ -91,8 +91,27 @@ export async function ensureCloudflared(log = console.log) {
|
|
|
91
91
|
return cached;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Kill cloudflared processes left over from previous runs against OUR port.
|
|
96
|
+
*
|
|
97
|
+
* WHY: the proxy exits with its terminal but cloudflared does not always go with
|
|
98
|
+
* it, so repeated `openzoo cursor` runs stack tunnels. Observed: two orphaned
|
|
99
|
+
* cloudflared processes, the editor configured with a third hostname, and every
|
|
100
|
+
* request answering 530/1033 — the editor was pointed at a tunnel whose origin
|
|
101
|
+
* had died while other live tunnels sat there serving nothing.
|
|
102
|
+
*/
|
|
103
|
+
function killStaleTunnels(port, log) {
|
|
104
|
+
try {
|
|
105
|
+
const out = execFileSync('pgrep', ['-f', `cloudflared tunnel --url http://localhost:${port}`], { encoding: 'utf8' });
|
|
106
|
+
const pids = out.split('\n').map((x) => x.trim()).filter(Boolean).filter((x) => Number(x) !== process.pid);
|
|
107
|
+
for (const pid of pids) { try { process.kill(Number(pid), 'SIGTERM'); } catch { /* already gone */ } }
|
|
108
|
+
if (pids.length) log(`tunnel: cleared ${pids.length} stale cloudflared process(es) from an earlier run`);
|
|
109
|
+
} catch { /* pgrep exits non-zero when nothing matches — that is the good case */ }
|
|
110
|
+
}
|
|
111
|
+
|
|
94
112
|
/** Start a quick tunnel to localhost:<port>; resolve with its public URL. */
|
|
95
113
|
export function startCloudflared(bin, port, log) {
|
|
114
|
+
killStaleTunnels(port, log);
|
|
96
115
|
return new Promise((resolve, reject) => {
|
|
97
116
|
// --edge-ip-version 4: cloudflared prefers IPv6+QUIC to reach the edge
|
|
98
117
|
// whenever the interface merely HAS an IPv6 address — it never checks the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.7",
|
|
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",
|