openzoo 0.30.0 → 0.30.1
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/cursorapi.js +53 -0
- package/lib/cursorbackend.js +4 -2
- package/lib/launch.js +14 -6
- package/package.json +1 -1
package/lib/cursorapi.js
CHANGED
|
@@ -92,6 +92,59 @@ export function encodeAvailableModels(models) {
|
|
|
92
92
|
return b.done();
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
* ENTITLEMENT RESPONSES. Measured from the backend log: once the transport works
|
|
97
|
+
* (NODE_TLS_REJECT_UNAUTHORIZED=0), the editor calls a set of DashboardService/
|
|
98
|
+
* AiService gRPC methods to decide the plan. Returning EMPTY reads as free tier —
|
|
99
|
+
* the "upgrade" prompt. These populate the exact protobuf shapes (read from the
|
|
100
|
+
* editor's own bundle) with an active pro plan and a real identity.
|
|
101
|
+
*/
|
|
102
|
+
|
|
103
|
+
// GetPlanInfoResponse{ 1 plan_info: PlanInfo{ 1 plan_name, 2 included_amount_cents, 5 plan_owner } }
|
|
104
|
+
export function encodeGetPlanInfo() {
|
|
105
|
+
const planInfo = new Buf()
|
|
106
|
+
.str(1, 'pro') // plan_name
|
|
107
|
+
.int(2, 99999900) // included_amount_cents (huge headroom)
|
|
108
|
+
.int(5, 1); // plan_owner = PLAN_OWNER_STRIPE
|
|
109
|
+
return new Buf().msg(1, planInfo).done();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// GetMeResponse{ 1 auth_id, 2 user_id, 3 email, 9 is_enterprise_user }
|
|
113
|
+
export function encodeGetMe() {
|
|
114
|
+
return new Buf()
|
|
115
|
+
.str(1, 'openzoo-user')
|
|
116
|
+
.int(2, 1)
|
|
117
|
+
.str(3, 'user@openzoo.local')
|
|
118
|
+
.bool(9, false)
|
|
119
|
+
.done();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// GetDefaultModelResponse{ 1 model, 2 thinking_model, 3 max_mode, 4 next_default_set_date }
|
|
123
|
+
export function encodeGetDefaultModel(models) {
|
|
124
|
+
const m = (models && models[0] && models[0].name) || 'gpt-4o';
|
|
125
|
+
return new Buf().str(1, m).str(2, m).bool(3, false).str(4, '').done();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// IsOnNewPricingResponse{ 1 is_on_new_pricing, 2 is_opted_out, 3 has_auto_spillover, 5 ... }
|
|
129
|
+
export function encodeIsOnNewPricing() {
|
|
130
|
+
return new Buf().bool(1, false).bool(2, false).bool(3, false).bool(5, false).done();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The bare proto body for a method, or null to fall through to empty-ok.
|
|
135
|
+
* Only the methods that gate entitlement or the model list are populated.
|
|
136
|
+
*/
|
|
137
|
+
export function encodeForMethod(method, models) {
|
|
138
|
+
switch (method) {
|
|
139
|
+
case 'AvailableModels': return encodeAvailableModels(models);
|
|
140
|
+
case 'GetPlanInfo': return encodeGetPlanInfo();
|
|
141
|
+
case 'GetMe': return encodeGetMe();
|
|
142
|
+
case 'GetDefaultModel': return encodeGetDefaultModel(models);
|
|
143
|
+
case 'IsOnNewPricing': return encodeIsOnNewPricing();
|
|
144
|
+
default: return null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
95
148
|
/** Connect unary framing: 5-byte prefix (flags + big-endian length) + payload. */
|
|
96
149
|
export function connectFrame(payload) {
|
|
97
150
|
const head = Buffer.alloc(5);
|
package/lib/cursorbackend.js
CHANGED
|
@@ -31,7 +31,7 @@ import fs from 'node:fs';
|
|
|
31
31
|
import os from 'node:os';
|
|
32
32
|
import path from 'node:path';
|
|
33
33
|
import { execFileSync } from 'node:child_process';
|
|
34
|
-
import { encodeAvailableModels } from './cursorapi.js';
|
|
34
|
+
import { encodeAvailableModels, encodeForMethod } from './cursorapi.js';
|
|
35
35
|
|
|
36
36
|
const TLS_DIR = path.join(os.homedir(), '.openzoo', 'cursor-tls');
|
|
37
37
|
const CURSOR_HOSTS = ['api2.cursor.sh', 'api3.cursor.sh', 'api4.cursor.sh', 'repo42.cursor.sh'];
|
|
@@ -134,7 +134,7 @@ function respond(req, res, method, models) {
|
|
|
134
134
|
return;
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
const bodyProto = method
|
|
137
|
+
const bodyProto = encodeForMethod(method, models) || Buffer.alloc(0);
|
|
138
138
|
|
|
139
139
|
if (isJson) {
|
|
140
140
|
// Connect unary JSON. AvailableModels as JSON; everything else an empty object.
|
|
@@ -196,8 +196,10 @@ export function startCursorBackend({ port = 8443, models, log = () => {} } = {})
|
|
|
196
196
|
log(`cursor-backend: #${conns} ${req.method} ${full} ct=${ct} body=${body.length}b`);
|
|
197
197
|
try {
|
|
198
198
|
respond(req, res, method, models);
|
|
199
|
+
const populated = ['GetPlanInfo', 'GetMe', 'GetDefaultModel', 'IsOnNewPricing'];
|
|
199
200
|
const what = /stripe|membership|subscription/i.test(full) ? 'ENTITLED stripe profile'
|
|
200
201
|
: method === 'AvailableModels' ? `AvailableModels (${models.length} models, gates open)`
|
|
202
|
+
: populated.includes(method) ? `${method} (PRO/entitled)`
|
|
201
203
|
: req.method === 'OPTIONS' ? 'CORS preflight 204'
|
|
202
204
|
: 'empty-ok';
|
|
203
205
|
log(`cursor-backend: -> ${what}`);
|
package/lib/launch.js
CHANGED
|
@@ -50,12 +50,20 @@ function resolveClaudeCli() {
|
|
|
50
50
|
*/
|
|
51
51
|
export async function launchClaude(argv) {
|
|
52
52
|
const base = `http://localhost:${config.port}/v1`;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
// AUTO-START THE PROXY. One command should just work — if nothing is listening,
|
|
54
|
+
// boot the proxy in THIS process (it stays alive because claude runs in the
|
|
55
|
+
// foreground below), rather than making the user run `npx openzoo` first.
|
|
56
|
+
let up = false;
|
|
57
|
+
try { up = (await fetch(`${base}/models`, { signal: AbortSignal.timeout(3000) })).ok; } catch { up = false; }
|
|
58
|
+
if (!up) {
|
|
59
|
+
console.error('openzoo: starting the proxy in the background...');
|
|
60
|
+
const { startProxy } = await import('./proxy.js');
|
|
61
|
+
await startProxy({ silent: true, autoTunnel: true });
|
|
62
|
+
for (let i = 0; i < 20 && !up; i++) {
|
|
63
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
64
|
+
try { up = (await fetch(`${base}/models`, { signal: AbortSignal.timeout(2000) })).ok; } catch { /* keep waiting */ }
|
|
65
|
+
}
|
|
66
|
+
if (!up) { console.error(`openzoo: proxy did not come up on ${base}`); process.exit(1); }
|
|
59
67
|
}
|
|
60
68
|
const terminal = argv.includes('--terminal') || argv.includes('-t');
|
|
61
69
|
const rest = argv.filter((a) => a !== '--terminal' && a !== '-t');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.30.
|
|
3
|
+
"version": "0.30.1",
|
|
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",
|