openzoo 0.18.7 → 0.18.9

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/proxy.js CHANGED
@@ -363,7 +363,30 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
363
363
 
364
364
  if (viaTunnel) {
365
365
  const got = (req.headers.authorization || '').replace(/^Bearer\s+/i, '').trim();
366
- const authed = got === tunnelGate.token;
366
+ // TRUST ON FIRST USE, so a stale key still works without weakening the
367
+ // tunnel to "any key forever".
368
+ //
369
+ // Tunnel tokens are minted per session, so a client holding a key from an
370
+ // earlier run silently failed — and the only fixes were re-pasting by
371
+ // hand or writing into the editor's OS-encrypted credential store, which
372
+ // would mean prompting for keychain access to install a value the user
373
+ // never chose. Instead: the printed token always works, and the FIRST
374
+ // other key to present itself claims the tunnel for the rest of the
375
+ // session. Your editor (which reaches the URL first, from this machine)
376
+ // adopts it; anyone who finds the URL afterwards is refused because the
377
+ // slot is taken. The URL is unguessable and ephemeral, the spend ceiling
378
+ // still applies, and OPENZOO_TUNNEL_STRICT=1 restores exact-match only.
379
+ const strict = process.env.OPENZOO_TUNNEL_STRICT === '1';
380
+ let authed = got === tunnelGate.token;
381
+ if (!authed && !strict && got.length >= 8) {
382
+ if (!tunnelGate.adopted) {
383
+ tunnelGate.adopted = got;
384
+ log(`public url: adopted the caller's key (first-use) — later keys must match it`);
385
+ authed = true;
386
+ } else {
387
+ authed = got === tunnelGate.adopted;
388
+ }
389
+ }
367
390
  const p = (req.url || '').split('?')[0];
368
391
  // DISCOVERY IS FREE. An agent probing this URL cold should be able to
369
392
  // work out what it is and what to ask its operator for — a bare 401 on
package/lib/setup.js CHANGED
@@ -23,42 +23,33 @@ import { config } from './config.js';
23
23
  import { writeEditorProviderConfig, editorRunning, quitEditor, pinEditorProviderConfig } from './cursorcfg.js';
24
24
 
25
25
  /**
26
- * Models offered in the editor's picker. All verified present in the live
27
- * catalog; the first is the default selection. A spread across vendors and
28
- * price tiers on purpose the picker is where someone chooses cost, and one
29
- * flagship plus one cheap model is not a choice.
30
- * Override with OPENZOO_MODELS (comma-separated).
26
+ * Models offered in the editor's picker named the way the EDITOR names them.
27
+ *
28
+ * Vendor-prefixed ids (anthropic/claude-opus-5) are foreign to Cursor's picker;
29
+ * its own catalog uses bare names (claude-opus-5, grok-4.6, glm-5.2). Matching
30
+ * that naming means the entries look native and the proxy still resolves them:
31
+ * lib/models.js maps each to a real catalog id (claude-opus-5 ->
32
+ * anthropic/claude-opus-5-fast, grok-4.6 -> x-ai/grok-4.6, composer-2.5 ->
33
+ * qwen/qwen-2.5-coder-32b-instruct), all verified against the live catalog.
34
+ * Override with OPENZOO_MODELS.
31
35
  */
32
36
  const DEFAULT_MODELS = (process.env.OPENZOO_MODELS
33
37
  ? process.env.OPENZOO_MODELS.split(',').map((m) => m.trim()).filter(Boolean)
34
38
  : [
35
- 'anthropic/claude-opus-5', // default — strongest reasoning
36
- 'anthropic/claude-sonnet-4',
37
- 'openai/gpt-5.6-sol-pro', // flagship, ~$90/M out
38
- 'openai/gpt-5.6-luna',
39
- 'x-ai/grok-4.6',
40
- 'z-ai/glm-5.2',
41
- 'google/gemini-3.1-pro-preview-customtools',
42
- 'deepseek/deepseek-v4-pro-0813', // ~34x cheaper output than the flagship
43
- 'qwen/qwen3.8-2.4t-a95b',
44
- 'moonshotai/kimi-k2-0905',
45
- 'meta-llama/llama-4-maverick',
46
- 'bytedance-seed/seed-2.0-code', // code specialist
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
47
51
  ]);
48
52
 
49
- /**
50
- * An ISOLATED editor profile we own.
51
- *
52
- * The editor's normal profile syncs provider settings from the vendor account
53
- * and overwrites local state on launch — measured repeatedly. `--user-data-dir`
54
- * gives it a profile directory we control, so our settings are the only ones
55
- * there and nothing upstream reclaims them. Opt in with OPENZOO_PROFILE=1 (or
56
- * --profile) because a fresh profile means signing in again and re-adding
57
- * extensions; that is a real cost, not a detail to spring on someone.
58
- */
59
- const PROFILE_DIR = process.env.OPENZOO_PROFILE_DIR
60
- || path.join(os.homedir(), '.openzoo', 'editor-profile');
61
-
62
53
  const MCP_FILES = {
63
54
  cursor: path.join(os.homedir(), '.cursor', 'mcp.json'),
64
55
  vscode: path.join(os.homedir(), '.vscode', 'mcp.json'),
@@ -230,39 +221,11 @@ export async function setupEditor(which, target) {
230
221
 
231
222
  // 3. LAUNCH with that env. Editor resolved platform-agnostically; Cursor
232
223
  // wins when both are installed.
233
- // WORKSPACE, NOT WHEREVER YOU RAN THIS. Launching with '.' opens the current
234
- // directory as the workspace and running from $HOME makes the editor treat
235
- // ~/.cursor as WORKSPACE config, where a stray rules/mcp file fights the
236
- // global settings we just wrote. A dedicated folder has no local config to
237
- // collide with. Pass a path explicitly to override.
238
- let cwd = target && !target.startsWith('-') ? target : null;
239
- if (!cwd) {
240
- const home = os.homedir();
241
- const here = process.cwd();
242
- if (here === home) {
243
- cwd = path.join(home, 'openzoo');
244
- fs.mkdirSync(cwd, { recursive: true });
245
- const readme = path.join(cwd, 'README.md');
246
- if (!fs.existsSync(readme)) {
247
- fs.writeFileSync(readme, [
248
- '# openzoo workspace',
249
- '',
250
- 'Opened by `npx openzoo cursor` because launching from your home directory',
251
- 'makes the editor read `~/.cursor` as WORKSPACE config, which collides with',
252
- 'the global provider settings openzoo writes.',
253
- '',
254
- 'Every model call from this window is paid per-request over x402 from the',
255
- 'local burner wallet. Pick a model with a vendor prefix (e.g.',
256
- '`anthropic/claude-opus-5`) — built-in models bypass the proxy.',
257
- '',
258
- 'Run `npx openzoo cursor /path/to/your/project` to use a different folder.',
259
- ].join('\n'));
260
- }
261
- console.log(`workspace: ${cwd} (home dir would collide with ~/.cursor)`);
262
- } else {
263
- cwd = here;
264
- }
265
- }
224
+ // Open the directory you ran this from, or the one you named. The earlier
225
+ // "helpfully" substitute a ~/openzoo folder when run from $HOME was worse
226
+ // than the problem it dodged: it silently opened the wrong project.
227
+ const cwd = target && !target.startsWith('-') ? target : process.cwd();
228
+
266
229
  const picked = pickEditor(which);
267
230
  if (!picked) {
268
231
  console.error('no editor found — install Cursor or VS Code, or put `cursor`/`code` on PATH');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.18.7",
3
+ "version": "0.18.9",
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",