openzoo 0.20.4 → 0.20.6

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
@@ -99,6 +99,19 @@ export function resolveModel(requested, ids) {
99
99
  const bare = String(requested).replace(/^openzoo[-/]/i, '');
100
100
  if (bare !== requested) {
101
101
  if (ids.includes(bare)) return bare;
102
+ // EXACT INVERSE OF MINTING. augmentModelList creates each twin as
103
+ // `openzoo-<suffix>` where suffix = id.split('/')[1], so the way back is a
104
+ // suffix match — not the similarity scorer below. Without this, a twin fell
105
+ // through to scoring and could land on a NEIGHBOUR of its own source:
106
+ // openzoo-claude-opus-5 resolved to anthropic/claude-opus-5-FAST, and with
107
+ // OPENZOO_DEFAULT_MODEL set in the environment every twin resolved to that
108
+ // one model instead — the user picked a model in the editor and silently got
109
+ // a different one. A twin is an EXPLICIT choice, so it is honoured before
110
+ // both the env override and the scorer.
111
+ const suffixed = ids.filter((id) => id.slice(id.indexOf('/') + 1) === bare);
112
+ // Full-strength before :free/:batch, mirroring the ordering everywhere else.
113
+ const exact = suffixed.find((id) => !id.includes(':')) || suffixed[0];
114
+ if (exact) return exact;
102
115
  requested = bare;
103
116
  }
104
117
  const env = process.env.OPENZOO_DEFAULT_MODEL;
package/lib/setup.js CHANGED
@@ -175,24 +175,39 @@ export async function setupEditor(which, target) {
175
175
  // the user staring at "waiting for tunnel.." while nothing else happened —
176
176
  // and if it never came up, the editor never launched at all. Announce it
177
177
  // when it arrives instead.
178
+ // THE EDITOR CONFIG CANNOT USE LOCALHOST. Cursor does not call the custom
179
+ // endpoint from your machine — its SERVER makes the request, so a private
180
+ // address is unreachable from there and it answers, verbatim:
181
+ // "Provider returned error: Access to private networks is forbidden"
182
+ // (see the header of lib/tunnel.js). So the public URL is a HARD dependency
183
+ // of the editor path, and we wait for it — bounded, with progress, because
184
+ // an unbounded wait once left the editor never launching at all.
178
185
  if (!publicUrl) {
179
- const started0 = started;
180
- (async () => {
181
- for (let i = 0; i < 240; i++) { // up to 2 min, in the background
182
- await new Promise((r) => setTimeout(r, 500));
183
- if (started0?.publicUrl) {
184
- console.log('');
185
- console.log(`tunnel: ${started0.publicUrl}/v1 api_key ${started0.tunnelToken}`);
186
- console.log(' (for a cloud-run harness — it cannot reach localhost)');
187
- return;
188
- }
189
- }
190
- console.log('tunnel: still not up — localhost is unaffected; OPENZOO_NO_TUNNEL=1 to skip it');
191
- })();
186
+ process.stdout.write('waiting for the public tunnel (the editor\'s server cannot reach localhost)');
187
+ for (let i = 0; i < 120 && !started?.publicUrl; i++) { // up to 60s
188
+ await new Promise((r) => setTimeout(r, 500));
189
+ if (i % 4 === 3) process.stdout.write('.');
190
+ }
191
+ console.log('');
192
+ publicUrl = started?.publicUrl ?? null;
193
+ tunnelKey = started?.tunnelToken ?? tunnelKey;
192
194
  }
195
+ if (publicUrl) console.log(`tunnel: ${publicUrl}/v1 api_key ${tunnelKey}`);
196
+ else console.log('tunnel: NOT up — the editor will not be able to reach the proxy\n (OPENZOO_NO_TUNNEL=1 to skip; terminal harnesses still work on localhost)');
193
197
  } else {
194
198
  console.log(`proxy already running on ${base}`);
199
+ // A proxy someone else started owns the tunnel; ask it for the public URL.
200
+ try {
201
+ const info = await (await fetch(`${base}/info`)).json();
202
+ publicUrl = (info?.publicTunnel || '').replace(/\/v1$/, '') || null;
203
+ tunnelKey = info?.tunnelToken ?? tunnelKey;
204
+ if (publicUrl) console.log(`tunnel: ${publicUrl}/v1 (from the running proxy)`);
205
+ } catch { /* no /info — fall through to the localhost warning below */ }
195
206
  }
207
+ // What the EDITOR is configured with. Localhost only as a last resort, and
208
+ // said out loud, because it will fail with the private-networks error.
209
+ const editorBase = publicUrl ? `${publicUrl}/v1` : base;
210
+ if (!publicUrl) console.log('settings: falling back to localhost — expect "Access to private networks is forbidden"');
196
211
 
197
212
  // 2. ENV INTO THE EDITOR. Both vendor shapes, so an OpenAI-compatible pane
198
213
  // and an Anthropic-shaped one (Claude Code extension) both route here.
@@ -230,14 +245,14 @@ export async function setupEditor(which, target) {
230
245
  }
231
246
  const models = await catalogModels(base);
232
247
  let wrote = null;
233
- try { wrote = writeEditorProviderConfig(target0, { baseUrl: base, models }); } catch (e) { wrote = { error: e.message }; }
248
+ try { wrote = writeEditorProviderConfig(target0, { baseUrl: editorBase, models, apiKey: tunnelKey }); } catch (e) { wrote = { error: e.message }; }
234
249
  if (wrote?.error) {
235
250
  console.log(`settings: could not write automatically (${wrote.error}) — set them in Settings → Models`);
236
251
  } else if (wrote) {
237
- console.log(`settings: openAIBaseUrl -> ${wrote.verified?.openAIBaseUrl || base}`);
252
+ console.log(`settings: openAIBaseUrl -> ${wrote.verified?.openAIBaseUrl || editorBase}`);
238
253
  console.log(' useOpenAIKey -> true');
239
254
  // PIN so the editor's account-sync cannot revert it on launch.
240
- const pin = pinEditorProviderConfig(target0, { baseUrl: base, models });
255
+ const pin = pinEditorProviderConfig(target0, { baseUrl: editorBase, models });
241
256
  console.log(` models -> ${models.join(', ')}`);
242
257
  console.log(` selected -> ${models[0]}`);
243
258
  console.log(pin?.pinned
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.20.4",
3
+ "version": "0.20.6",
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",