openzoo 0.29.6 → 0.29.8

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.
@@ -26,7 +26,7 @@
26
26
  * content-type and answer in kind: bare message for connect unary proto, enveloped
27
27
  * + trailers for grpc-web. Both are handled below.
28
28
  */
29
- import https from 'node:https';
29
+ import http2 from 'node:http2';
30
30
  import fs from 'node:fs';
31
31
  import os from 'node:os';
32
32
  import path from 'node:path';
@@ -172,17 +172,19 @@ function respond(req, res, method, models) {
172
172
  export function startCursorBackend({ port = 8443, models, log = () => {} } = {}) {
173
173
  const { cert, key } = ensureCert(log);
174
174
  let conns = 0;
175
- // HTTP/1.1 ONLY, but with ALPN advertising http/1.1. Two measured facts drove
176
- // this: (1) with NO ALPN the client aborts with ERR_SSL_NO_APPLICATION_PROTOCOL
177
- // it requires the server to name a protocol; (2) when we offered h2, the
178
- // editor's h2 connections RESET (ECONNRESET wall) while its h1 calls completed
179
- // (requests #1-5). Connect-RPC and gRPC-web both work over h1, so we advertise
180
- // ONLY http/1.1: the client negotiates it and every call completes.
181
- const server = https.createServer(
175
+ // SERVE BOTH h2 AND h1. An earlier build forced h1-only after concluding the
176
+ // editor's h2 connections reset but that log was the STALE 8443 backend the
177
+ // pf redirect was still intercepting, not this server, so the conclusion was
178
+ // contaminated. Measured cleanly now: some editor connections offer ONLY h2
179
+ // (ERR_SSL_NO_APPLICATION_PROTOCOL when we advertise just h1), so we must
180
+ // negotiate both. allowHTTP1 keeps the h1 fetch() calls (stripe/updates)
181
+ // working; ALPN h2 first satisfies the Connect gRPC client.
182
+ const server = http2.createSecureServer(
182
183
  {
183
184
  cert: fs.readFileSync(cert),
184
185
  key: fs.readFileSync(key),
185
- ALPNProtocols: ['http/1.1'],
186
+ allowHTTP1: true,
187
+ ALPNProtocols: ['h2', 'http/1.1'],
186
188
  SNICallback: (servername, cb) => { log(`cursor-tls: <- ClientHello SNI=${servername}`); cb(null); },
187
189
  },
188
190
  async (req, res) => {
package/lib/hosts.js CHANGED
@@ -161,9 +161,16 @@ export function bindBackend443(modelsPath, logPath, log = console.log) {
161
161
  // broken one kept answering — the reason a fix looked like it never landed.
162
162
  // We SIGKILL by name AND by whoever holds 443, poll until the port is actually
163
163
  // free (up to ~5s), then start the new one.
164
+ // FLUSH THE OLD pfctl 443->8443 REDIRECT FIRST. Versions 0.29.0-0.29.2 installed
165
+ // a pf anchor that redirects 443 to a stale user-process backend on 8443. While
166
+ // that rule is loaded, pf intercepts 443 BEFORE our new root bind ever sees it,
167
+ // so every fix looked like it never landed. Flush the anchor + kill 8443 + kill
168
+ // 443 by name and by port, wait for both to free, THEN bind.
169
+ const flushPf = process.platform === 'darwin' ? 'pfctl -a openzoo -F all 2>/dev/null; ' : '';
164
170
  const ok = privileged(
165
- `pkill -9 -f 'cursor-backend.js' 2>/dev/null; `
166
- + `for p in $(lsof -nP -iTCP:443 -sTCP:LISTEN -t 2>/dev/null); do kill -9 "$p" 2>/dev/null; done; `
171
+ flushPf
172
+ + `pkill -9 -f 'cursor-backend.js' 2>/dev/null; `
173
+ + `for p in $(lsof -nP -iTCP:443 -sTCP:LISTEN -t 2>/dev/null) $(lsof -nP -iTCP:8443 -sTCP:LISTEN -t 2>/dev/null); do kill -9 "$p" 2>/dev/null; done; `
167
174
  + `for i in 1 2 3 4 5 6 7 8 9 10; do lsof -nP -iTCP:443 -sTCP:LISTEN -t >/dev/null 2>&1 || break; sleep 0.5; done; `
168
175
  + `nohup '${node}' '${script}' 443 '${modelsPath}' '${logPath}' >/dev/null 2>&1 & `
169
176
  + 'sleep 1; true',
@@ -173,5 +180,10 @@ export function bindBackend443(modelsPath, logPath, log = console.log) {
173
180
 
174
181
  export function unbindBackend443() {
175
182
  if (WIN) return { ok: false, manual: true };
176
- return { ok: privileged("pkill -f 'cursor-backend.js' 2>/dev/null; true") };
183
+ const flushPf = process.platform === 'darwin' ? 'pfctl -a openzoo -F all 2>/dev/null; ' : '';
184
+ return { ok: privileged(
185
+ flushPf
186
+ + "pkill -9 -f 'cursor-backend.js' 2>/dev/null; "
187
+ + 'for p in $(lsof -nP -iTCP:8443 -sTCP:LISTEN -t 2>/dev/null); do kill -9 "$p" 2>/dev/null; done; true',
188
+ ) };
177
189
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.29.6",
3
+ "version": "0.29.8",
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",