openzoo 0.9.5 → 0.9.7

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
@@ -137,12 +137,27 @@ export const ALIAS_IDS = [
137
137
  'deepseek-chat', 'deepseek-reasoner', 'qwen-max', 'llama-3.3-70b',
138
138
  ];
139
139
 
140
- /** Merge alias rows into a /v1/models payload without duplicating real ids. */
140
+ /**
141
+ * Merge alias rows into a /v1/models payload without duplicating real ids.
142
+ * Each alias inherits context_length and pricing from the model it RESOLVES
143
+ * to — a harness sizing its corpus off "gpt-4o" gets the real ceiling of the
144
+ * model that will actually serve it, not a blank.
145
+ */
141
146
  export function augmentModelList(payload) {
142
147
  const data = Array.isArray(payload?.data) ? payload.data : [];
143
148
  const have = new Set(data.map((m) => m.id));
144
- const aliases = ALIAS_IDS.filter((id) => !have.has(id))
145
- .map((id) => ({ id, object: 'model', owned_by: 'openzoo-alias' }));
149
+ const ids = data.map((m) => m.id);
150
+ const aliases = ALIAS_IDS.filter((id) => !have.has(id)).map((id) => {
151
+ const target = data.find((m) => m.id === resolveModel(id, ids));
152
+ return {
153
+ id,
154
+ object: 'model',
155
+ owned_by: 'openzoo-alias',
156
+ ...(target?.context_length ? { context_length: target.context_length, context_window: target.context_window ?? target.context_length } : {}),
157
+ ...(target?.pricing ? { pricing: target.pricing } : {}),
158
+ ...(target ? { served_by: target.id } : {}),
159
+ };
160
+ });
146
161
  return { ...payload, object: payload?.object || 'list', data: [...data, ...aliases] };
147
162
  }
148
163
 
package/lib/proxy.js CHANGED
@@ -271,12 +271,37 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
271
271
  }
272
272
  if (viaTunnel) {
273
273
  const got = (req.headers.authorization || '').replace(/^Bearer\s+/i, '').trim();
274
- if (got !== tunnelGate.token) {
275
- log(`public url: 401 ${req.method} ${req.url}`);
276
- jsonErr(res, 401, 'unauthorized: this openzoo public URL requires the api key printed at startup');
277
- return;
274
+ const authed = got === tunnelGate.token;
275
+ const p = (req.url || '').split('?')[0];
276
+ // DISCOVERY IS FREE. An agent probing this URL cold should be able to
277
+ // work out what it is and what to ask its operator for — a bare 401 on
278
+ // every path just sends it spelunking through the operator's machine.
279
+ // Reads that cost nothing and leak nothing (the catalog is public on
280
+ // the zoo anyway) go through without the key; money paths stay gated.
281
+ if (!authed) {
282
+ if (req.method === 'GET' && (p === '/' || p === '/v1' || p === '/v1/info')) {
283
+ res.writeHead(200, { 'content-type': 'application/json' });
284
+ res.end(JSON.stringify({
285
+ name: 'openzoo proxy (public tunnel)',
286
+ what: 'OpenAI-compatible pay-per-call proxy — the operator\'s wallet pays per request via x402; no account, no signup.',
287
+ authentication: 'All POST endpoints require `Authorization: Bearer <api key>`. The key is printed in the operator\'s terminal at proxy startup — ask them for it. It is NOT guessable.',
288
+ endpoints: {
289
+ 'GET /v1/models': 'model catalog with pricing + context_length — no key needed',
290
+ 'GET /v1/models/{id}': 'single-model probe — no key needed',
291
+ 'POST /v1/chat/completions': 'chat (streaming supported, any model id — unknown ids are matched to the nearest served model) — key required',
292
+ },
293
+ docs: 'https://openzoo.fun · https://www.npmjs.com/package/openzoo',
294
+ }, null, 2));
295
+ return;
296
+ }
297
+ const freeRead = req.method === 'GET' && (p === '/v1/models' || p.startsWith('/v1/models/'));
298
+ if (!freeRead) {
299
+ log(`public url: 401 ${req.method} ${req.url}`);
300
+ jsonErr(res, 401, 'unauthorized: this openzoo public URL requires the api key printed at the operator\'s proxy startup — GET / for discovery, GET /v1/models is open');
301
+ return;
302
+ }
278
303
  }
279
- if (tunnelSpent >= tunnelGate.sessionMaxUsd) {
304
+ if (authed && tunnelSpent >= tunnelGate.sessionMaxUsd) {
280
305
  jsonErr(res, 402, `openzoo public-URL session cap reached ($${tunnelGate.sessionMaxUsd}) — restart the proxy or raise OPENZOO_TUNNEL_MAX_USD`);
281
306
  return;
282
307
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.9.5",
3
+ "version": "0.9.7",
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",