openzoo 0.30.3 → 0.30.5
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/cursorcfg.js +42 -2
- package/lib/setup.js +9 -1
- package/package.json +1 -1
package/lib/cursorcfg.js
CHANGED
|
@@ -144,6 +144,14 @@ export function writeEditorProviderConfig(which, { baseUrl, models }) {
|
|
|
144
144
|
const before = { openAIBaseUrl: doc.openAIBaseUrl, models: (doc.availableAPIKeyModels || []).length };
|
|
145
145
|
doc.openAIBaseUrl = baseUrl;
|
|
146
146
|
doc.useOpenAIKey = true;
|
|
147
|
+
// THE MODEL-SELECTION GATE READS MEMBERSHIP FROM RIGHT HERE. Traced in the
|
|
148
|
+
// bundle: membershipType(){ return this.reactiveStorageService
|
|
149
|
+
// .applicationUserPersistentStorage.membershipType } — i.e. THIS blob, not
|
|
150
|
+
// cursorAuth/stripeMembershipType. Setting it entitles model selection; the
|
|
151
|
+
// api2 impersonation keeps the editor from re-syncing it back to free.
|
|
152
|
+
// OPENZOO_MEMBERSHIP overrides (pro | pro_plus | enterprise | ultra).
|
|
153
|
+
doc.membershipType = process.env.OPENZOO_MEMBERSHIP || 'pro';
|
|
154
|
+
doc.subscriptionStatus = 'active';
|
|
147
155
|
// Merge, don't clobber: a user may have their own custom models listed.
|
|
148
156
|
const existing = Array.isArray(doc.availableAPIKeyModels) ? doc.availableAPIKeyModels : [];
|
|
149
157
|
const names = new Set(existing.map((m) => (typeof m === 'string' ? m : m?.name)).filter(Boolean));
|
|
@@ -257,6 +265,7 @@ export function writeEditorProviderConfig(which, { baseUrl, models }) {
|
|
|
257
265
|
* that clobbers them. Removable with unpinEditorProviderConfig.
|
|
258
266
|
*/
|
|
259
267
|
export function pinEditorProviderConfig(which, { baseUrl, models }) {
|
|
268
|
+
const mem = process.env.OPENZOO_MEMBERSHIP || 'pro';
|
|
260
269
|
const db = storagePath(which);
|
|
261
270
|
if (!fs.existsSync(db)) return null;
|
|
262
271
|
const esc = (v) => String(v).replace(/'/g, "''");
|
|
@@ -283,7 +292,8 @@ CREATE TRIGGER openzoo_pin AFTER UPDATE ON ItemTable
|
|
|
283
292
|
WHEN NEW.key = '${KEY}'
|
|
284
293
|
AND (json_extract(NEW.value,'$.openAIBaseUrl') IS NOT '${base}'
|
|
285
294
|
OR json_extract(NEW.value,'$.featureModelConfigs.composer.defaultModel') IS NOT '${primary}'
|
|
286
|
-
OR json_array_length(json_extract(NEW.value,'$.availableDefaultModels2')) IS NOT ${models.length}
|
|
295
|
+
OR json_array_length(json_extract(NEW.value,'$.availableDefaultModels2')) IS NOT ${models.length}
|
|
296
|
+
OR json_extract(NEW.value,'$.membershipType') IS NOT '${mem}')
|
|
287
297
|
BEGIN
|
|
288
298
|
UPDATE ItemTable SET value = json_set(
|
|
289
299
|
NEW.value,
|
|
@@ -292,7 +302,9 @@ BEGIN
|
|
|
292
302
|
'$.availableAPIKeyModels', json('${modelJson}'),
|
|
293
303
|
'$.availableDefaultModels2', json('${uiJson}'),
|
|
294
304
|
'$.featureModelConfigs.composer.defaultModel', '${primary}',
|
|
295
|
-
'$.featureModelConfigs.cmdK.defaultModel', '${primary}'
|
|
305
|
+
'$.featureModelConfigs.cmdK.defaultModel', '${primary}',
|
|
306
|
+
'$.membershipType', '${mem}',
|
|
307
|
+
'$.subscriptionStatus', 'active'
|
|
296
308
|
) WHERE key = NEW.key;
|
|
297
309
|
END;`;
|
|
298
310
|
try { sqlite(db, sql); } catch (e) { return { error: e.message }; }
|
|
@@ -306,3 +318,31 @@ export function unpinEditorProviderConfig(which) {
|
|
|
306
318
|
try { sqlite(db, 'DROP TRIGGER IF EXISTS openzoo_pin;'); return { unpinned: true }; }
|
|
307
319
|
catch (e) { return { error: e.message }; }
|
|
308
320
|
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Force the CACHED membership the editor stores in its own auth table to an
|
|
324
|
+
* entitled value. MEASURED: the working (ultra) machine held
|
|
325
|
+
* `cursorAuth/stripeMembershipType = "ultra"` right here in ItemTable, the free
|
|
326
|
+
* machine held "free" — and the model-selection gate reads this cached value,
|
|
327
|
+
* NOT the api2 responses (which we already answer entitled, to no effect). The
|
|
328
|
+
* api2 impersonation's job is then to stop the editor RE-SYNCING this back to
|
|
329
|
+
* free on the next focus. Best-effort; returns what it set.
|
|
330
|
+
*/
|
|
331
|
+
export function forceMembership(which, type = 'pro') {
|
|
332
|
+
const db = storagePath(which);
|
|
333
|
+
if (!db || !fs.existsSync(db)) return { error: 'no editor db' };
|
|
334
|
+
try { execFileSync('sqlite3', ['-version'], { stdio: 'ignore' }); } catch { return { error: 'sqlite3 not available' }; }
|
|
335
|
+
const q = (k) => `'${String(k).replace(/'/g, "''")}'`;
|
|
336
|
+
const set = (key, val) => {
|
|
337
|
+
// These auth values are stored as raw JSON strings (quoted), e.g. "ultra".
|
|
338
|
+
const json = JSON.stringify(String(val));
|
|
339
|
+
sqlite(db, `INSERT INTO ItemTable(key,value) VALUES(${q(key)},${q(json)}) `
|
|
340
|
+
+ `ON CONFLICT(key) DO UPDATE SET value=${q(json)};`);
|
|
341
|
+
};
|
|
342
|
+
try {
|
|
343
|
+
set('cursorAuth/stripeMembershipType', type);
|
|
344
|
+
set('cursorAuth/stripeSubscriptionStatus', 'active');
|
|
345
|
+
const got = sqlite(db, `SELECT value FROM ItemTable WHERE key='cursorAuth/stripeMembershipType';`).trim();
|
|
346
|
+
return { set: type, verified: got };
|
|
347
|
+
} catch (e) { return { error: e.message }; }
|
|
348
|
+
}
|
package/lib/setup.js
CHANGED
|
@@ -22,7 +22,7 @@ import { spawn } from 'node:child_process';
|
|
|
22
22
|
import { config } from './config.js';
|
|
23
23
|
import {
|
|
24
24
|
writeEditorProviderConfig, editorRunning, quitEditor,
|
|
25
|
-
pinEditorProviderConfig, unpinEditorProviderConfig,
|
|
25
|
+
pinEditorProviderConfig, unpinEditorProviderConfig, forceMembership,
|
|
26
26
|
} from './cursorcfg.js';
|
|
27
27
|
|
|
28
28
|
/**
|
|
@@ -503,6 +503,14 @@ export async function setupEditor(which, target) {
|
|
|
503
503
|
+ ' The 443->8443 redirect is not delivering (pfctl/loopback). Tell me and I switch to a root-bound 443.');
|
|
504
504
|
} catch (e) { console.log(`takeover: self-test error (${e.message})`); }
|
|
505
505
|
|
|
506
|
+
// WRITE THE CACHED MEMBERSHIP the model-gate actually reads (measured: the
|
|
507
|
+
// ultra machine held "ultra" here; api2 responses alone did not move the
|
|
508
|
+
// gate). The impersonation keeps it from re-syncing back to free.
|
|
509
|
+
try {
|
|
510
|
+
const fm = forceMembership(target0, process.env.OPENZOO_MEMBERSHIP || 'pro');
|
|
511
|
+
console.log(fm.error ? `takeover: membership write skipped (${fm.error})`
|
|
512
|
+
: `takeover: cached membership -> ${fm.verified} (was gating model selection)`);
|
|
513
|
+
} catch (e) { console.log(`takeover: membership write failed (${e.message})`); }
|
|
506
514
|
console.log('takeover: launching with --ignore-certificate-errors so the self-signed cert is trusted (no CA install)');
|
|
507
515
|
} catch (e) {
|
|
508
516
|
console.log(`takeover: failed to start (${e.message}) — falling back to plain routing`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.30.
|
|
3
|
+
"version": "0.30.5",
|
|
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",
|