openzoo 0.29.0 → 0.29.2

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.
@@ -126,23 +126,37 @@ function respond(req, res, method, models) {
126
126
  */
127
127
  export function startCursorBackend({ port = 8443, models, log = () => {} } = {}) {
128
128
  const { cert, key } = ensureCert(log);
129
+ let conns = 0;
129
130
  const server = https.createServer(
130
- { cert: fs.readFileSync(cert), key: fs.readFileSync(key) },
131
+ {
132
+ cert: fs.readFileSync(cert),
133
+ key: fs.readFileSync(key),
134
+ // Log which hostname the editor asked for on the TLS handshake — this is
135
+ // the single clearest proof the redirect is working and the editor is
136
+ // reaching US instead of the real backend.
137
+ SNICallback: (servername, cb) => { log(`cursor-tls: <- ClientHello SNI=${servername}`); cb(null); },
138
+ },
131
139
  async (req, res) => {
132
- // Connect method is the last path segment: /aiserver.v1.XService/Method
133
- const method = (req.url || '').split('/').filter(Boolean).pop() || '';
134
- await readBody(req);
140
+ conns += 1;
141
+ const full = req.url || '';
142
+ const method = full.split('/').filter(Boolean).pop() || '';
143
+ const ct = req.headers['content-type'] || '?';
144
+ const body = await readBody(req);
145
+ log(`cursor-backend: #${conns} ${req.method} ${full} ct=${ct} body=${body.length}b`);
135
146
  try {
136
147
  respond(req, res, method, models);
137
- if (method === 'AvailableModels') log(`cursor-backend: served AvailableModels (${models.length} models, all gates open)`);
148
+ log(`cursor-backend: -> ${method === 'AvailableModels' ? `AvailableModels (${models.length} models, gates open)` : 'empty-ok'}`);
138
149
  } catch (e) {
139
150
  res.writeHead(200, { 'content-type': 'application/proto', 'grpc-status': '0' });
140
151
  res.end(Buffer.alloc(0));
141
- log(`cursor-backend: ${method} -> empty-ok (${e.message})`);
152
+ log(`cursor-backend: -> empty-ok (err ${e.message})`);
142
153
  }
143
154
  },
144
155
  );
145
- server.on('tlsClientError', () => { /* editor probing; ignore */ });
156
+ // NEVER swallow this a rejected handshake is exactly the failure to see.
157
+ server.on('tlsClientError', (e, sock) => {
158
+ log(`cursor-tls: HANDSHAKE FAILED ${e.code || e.message} (peer ${sock?.remoteAddress || '?'})`);
159
+ });
146
160
  server.listen(port, '127.0.0.1', () => log(`cursor-backend: listening on 127.0.0.1:${port} as ${CURSOR_HOSTS[0]}`));
147
161
  return { server, port };
148
162
  }
package/lib/setup.js CHANGED
@@ -448,7 +448,11 @@ export async function setupEditor(which, target) {
448
448
  // ourselves: catalog with every gate open, empty-valid for the rest. No CA
449
449
  // install — the editor is launched with --ignore-certificate-errors below,
450
450
  // and it does not pin certs (verified), so a self-signed cert is accepted.
451
- if (target0 === 'cursor' && process.argv.includes('--takeover')) {
451
+ // DEFAULT ON. Takeover is what makes a plan-less account route, and it is
452
+ // harmless on an entitled one (the editor just reads our catalog instead of
453
+ // theirs), so it runs unless explicitly disabled with --no-takeover.
454
+ const doTakeover = target0 === 'cursor' && !process.argv.includes('--no-takeover');
455
+ if (doTakeover) {
452
456
  try {
453
457
  const { startCursorBackend } = await import('./cursorbackend.js');
454
458
  const { redirect443 } = await import('./hosts.js');
@@ -458,6 +462,31 @@ export async function setupEditor(which, target) {
458
462
  console.log(rr.ok ? 'takeover: 443 -> 8443 redirect installed (npx openzoo unblock reverts)'
459
463
  : rr.manual ? 'takeover: apply the redirect above, then relaunch'
460
464
  : 'takeover: could NOT install the 443 redirect — impersonation will not receive traffic');
465
+
466
+ // SELF-TEST — prove the whole path (hosts -> redirect -> our server) works
467
+ // NOW, at startup, so a failure is a loud line here instead of a silent
468
+ // "still gated" in the editor. We dial api2.cursor.sh exactly as the editor
469
+ // will; if our backend logs the ClientHello and answers, it is wired.
470
+ await new Promise((r) => setTimeout(r, 400));
471
+ try {
472
+ const https = await import('node:https');
473
+ const ok = await new Promise((resolve) => {
474
+ const rq = https.request({
475
+ host: 'api2.cursor.sh', port: 443, method: 'POST',
476
+ path: '/aiserver.v1.AiService/AvailableModels',
477
+ headers: { 'content-type': 'application/grpc-web+proto' },
478
+ rejectUnauthorized: false, timeout: 4000,
479
+ }, (rp) => { rp.on('data', () => {}); rp.on('end', () => resolve(rp.statusCode === 200)); });
480
+ rq.on('error', () => resolve(false));
481
+ rq.on('timeout', () => { rq.destroy(); resolve(false); });
482
+ rq.end();
483
+ });
484
+ console.log(ok
485
+ ? 'takeover: SELF-TEST PASS — api2.cursor.sh:443 reaches our backend. The editor will too.'
486
+ : 'takeover: SELF-TEST FAIL — api2.cursor.sh:443 did NOT reach our backend.\n'
487
+ + ' The 443->8443 redirect is not delivering (pfctl/loopback). Tell me and I switch to a root-bound 443.');
488
+ } catch (e) { console.log(`takeover: self-test error (${e.message})`); }
489
+
461
490
  console.log('takeover: launching with --ignore-certificate-errors so the self-signed cert is trusted (no CA install)');
462
491
  } catch (e) {
463
492
  console.log(`takeover: failed to start (${e.message}) — falling back to plain routing`);
@@ -497,7 +526,7 @@ export async function setupEditor(which, target) {
497
526
  const args = [cwd];
498
527
  // Trust our self-signed impersonation cert without any CA install — this flag
499
528
  // is the entire reason no trust prompt is needed. Only added under --takeover.
500
- if (target0 === 'cursor' && process.argv.includes('--takeover')) args.unshift('--ignore-certificate-errors');
529
+ if (target0 === 'cursor' && !process.argv.includes('--no-takeover')) args.unshift('--ignore-certificate-errors');
501
530
  if (useProfile) {
502
531
  fs.mkdirSync(PROFILE_DIR, { recursive: true });
503
532
  args.unshift(`--user-data-dir=${PROFILE_DIR}`, `--extensions-dir=${path.join(PROFILE_DIR, 'extensions')}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.29.0",
3
+ "version": "0.29.2",
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",