openzoo 0.18.10 → 0.19.0

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/models.js CHANGED
@@ -90,6 +90,17 @@ const tokensOf = (id) => id.toLowerCase().split(/[^a-z0-9.]+/).filter((t) => t &
90
90
  */
91
91
  export function resolveModel(requested, ids) {
92
92
  if (!requested || !ids?.length || ids.includes(requested)) return null;
93
+ // `openzoo-` prefixed names exist so an editor cannot mistake them for its
94
+ // OWN models: Cursor claims any name in its catalog (claude-opus-5, grok-4.6)
95
+ // and routes it to its backend, ignoring the custom endpoint entirely —
96
+ // measured, zero connections ever reached the proxy. A name it does not know
97
+ // is forced down the custom path. Strip the marker before matching so
98
+ // openzoo-opus-5 still resolves to anthropic/claude-opus-5.
99
+ const bare = String(requested).replace(/^openzoo[-/]/i, '');
100
+ if (bare !== requested) {
101
+ if (ids.includes(bare)) return bare;
102
+ requested = bare;
103
+ }
93
104
  const env = process.env.OPENZOO_DEFAULT_MODEL;
94
105
  if (env && ids.includes(env)) return env;
95
106
 
@@ -147,6 +158,32 @@ export function augmentModelList(payload) {
147
158
  const data = Array.isArray(payload?.data) ? payload.data : [];
148
159
  const have = new Set(data.map((m) => m.id));
149
160
  const ids = data.map((m) => m.id);
161
+ // openzoo-* twins of the popular models. An editor that validates a custom
162
+ // model against THIS list (Cursor's "Add model" box reports "No models
163
+ // available" for anything missing here) can only offer what we publish — and
164
+ // the openzoo- prefix is what stops it claiming the name as one of its own
165
+ // built-ins and routing to its backend instead of to us.
166
+ const branded = [];
167
+ for (const src0 of data) {
168
+ const id = src0.id;
169
+ // Brand only REAL upstream models. Anything we synthesised (a twin or a
170
+ // harness alias) must be skipped, or augmenting an already-augmented
171
+ // payload mints openzoo-openzoo-* and the catalog grows every pass.
172
+ if (!id || id.startsWith('openzoo-') || String(src0.owned_by || '').startsWith('openzoo')) continue;
173
+ const short = id.includes('/') ? id.split('/')[1] : id;
174
+ const name = `openzoo-${short}`;
175
+ if (have.has(name)) continue;
176
+ const src = data.find((m) => m.id === id);
177
+ branded.push({
178
+ id: name,
179
+ object: 'model',
180
+ owned_by: 'openzoo',
181
+ served_by: id,
182
+ ...(src?.context_length ? { context_length: src.context_length, context_window: src.context_window ?? src.context_length } : {}),
183
+ ...(src?.pricing ? { pricing: src.pricing } : {}),
184
+ });
185
+ have.add(name);
186
+ }
150
187
  const aliases = ALIAS_IDS.filter((id) => !have.has(id)).map((id) => {
151
188
  const target = data.find((m) => m.id === resolveModel(id, ids));
152
189
  return {
@@ -158,7 +195,7 @@ export function augmentModelList(payload) {
158
195
  ...(target ? { served_by: target.id } : {}),
159
196
  };
160
197
  });
161
- return { ...payload, object: payload?.object || 'list', data: [...data, ...aliases] };
198
+ return { ...payload, object: payload?.object || 'list', data: [...data, ...branded, ...aliases] };
162
199
  }
163
200
 
164
201
  /**
package/lib/setup.js CHANGED
@@ -36,18 +36,18 @@ import { writeEditorProviderConfig, editorRunning, quitEditor, pinEditorProvider
36
36
  const DEFAULT_MODELS = (process.env.OPENZOO_MODELS
37
37
  ? process.env.OPENZOO_MODELS.split(',').map((m) => m.trim()).filter(Boolean)
38
38
  : [
39
- 'claude-opus-5', // default
40
- 'claude-sonnet-5',
41
- 'gpt-5.6-sol',
42
- 'gpt-5.6-luna',
43
- 'grok-4.6',
44
- 'glm-5.2',
45
- 'gemini-3.1-pro',
46
- 'kimi-k3',
47
- 'gpt-5.3-codex',
48
- 'claude-haiku-4-5',
49
- 'composer-2.5',
50
- 'deepseek/deepseek-v4-pro-0813', // no Cursor equivalent; cheapest output
39
+ 'openzoo-claude-opus-5',
40
+ 'openzoo-claude-sonnet-5',
41
+ 'openzoo-gpt-5.6-sol-pro',
42
+ 'openzoo-grok-4.6',
43
+ 'openzoo-glm-5.2',
44
+ 'openzoo-gemini-3.1-pro-preview-customtools',
45
+ 'openzoo-kimi-k3',
46
+ 'openzoo-deepseek-v4-pro-0813',
47
+ 'openzoo-qwen3.8-2.4t-a95b',
48
+ 'openzoo-gpt-5.3-codex',
49
+ 'openzoo-claude-haiku-4.5',
50
+ 'openzoo-seed-2.0-code',
51
51
  ]);
52
52
 
53
53
  const MCP_FILES = {
@@ -152,24 +152,27 @@ export async function setupEditor(which, target) {
152
152
  const started = await startProxy({ silent: true, autoTunnel: true });
153
153
  publicUrl = started?.publicUrl ?? null;
154
154
  tunnelKey = started?.tunnelToken ?? null;
155
- // Wait for cloudflared to publish the URL. It downloads on first run and
156
- // routinely takes 30-45s; a 10s wait meant the tunnel line simply never
157
- // printed and the user never learned the public URL existed.
158
- process.stdout.write('waiting for tunnel');
159
- let tunnelErr = null;
160
- for (let i = 0; i < 120 && !publicUrl; i++) {
161
- await new Promise((r) => setTimeout(r, 500));
162
- publicUrl = started?.publicUrl ?? null;
163
- tunnelKey = started?.tunnelToken ?? null;
164
- // Bail the moment it FAILS. Polling to the full timeout on a dead tunnel
165
- // just looks like a hang — and silent:true hides the proxy's own error.
166
- tunnelErr = started?.tunnelError ?? null;
167
- if (tunnelErr) break;
168
- if (i % 4 === 3) process.stdout.write('.');
155
+ // DO NOT BLOCK ON THE TUNNEL. Everything below (settings, MCP, launching
156
+ // the editor) is local and works without it; only a CLOUD-run harness needs
157
+ // the public URL. Waiting inline meant a slow or failing cloudflared left
158
+ // the user staring at "waiting for tunnel.." while nothing else happened —
159
+ // and if it never came up, the editor never launched at all. Announce it
160
+ // when it arrives instead.
161
+ if (!publicUrl) {
162
+ const started0 = started;
163
+ (async () => {
164
+ for (let i = 0; i < 240; i++) { // up to 2 min, in the background
165
+ await new Promise((r) => setTimeout(r, 500));
166
+ if (started0?.publicUrl) {
167
+ console.log('');
168
+ console.log(`tunnel: ${started0.publicUrl}/v1 api_key ${started0.tunnelToken}`);
169
+ console.log(' (for a cloud-run harness — it cannot reach localhost)');
170
+ return;
171
+ }
172
+ }
173
+ console.log('tunnel: still not up — localhost is unaffected; OPENZOO_NO_TUNNEL=1 to skip it');
174
+ })();
169
175
  }
170
- if (publicUrl) process.stdout.write(' ok\n');
171
- else if (tunnelErr) process.stdout.write(` failed: ${tunnelErr}\n (localhost still works; OPENZOO_NO_TUNNEL=1 skips this)\n`);
172
- else process.stdout.write(' timed out — carrying on; it will print here if it comes up later\n');
173
176
  } else {
174
177
  console.log(`proxy already running on ${base}`);
175
178
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.18.10",
3
+ "version": "0.19.0",
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",