myapikey 0.29.0 → 0.30.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myapikey",
3
- "version": "0.29.0",
3
+ "version": "0.30.0",
4
4
  "type": "module",
5
5
  "description": "Personal LLM API gateway & proxy — one address + one API key for all your models. Forwards OpenAI & Anthropic calls to your backends with failover and a circuit breaker. Pure passthrough, no format translation. Self-hosted (CLI + web UI).",
6
6
  "keywords": [
@@ -76,6 +76,7 @@ function toPublic(p: Provider) {
76
76
  apiKey: mask(p.apiKey),
77
77
  rpm: p.rpm ?? 0,
78
78
  discoveredModels: p.discoveredModels ?? [],
79
+ extraModels: p.extraModels ?? [],
79
80
  discoveredAt: p.discoveredAt ?? null,
80
81
  createdAt: p.createdAt,
81
82
  };
@@ -323,6 +324,33 @@ export function adminApi(store: Store, auth: MiddlewareHandler, openai: Hono, an
323
324
  }
324
325
  });
325
326
 
327
+ // Manual supplement to discovery: upstream model ids a backend's /models list
328
+ // doesn't include but that still work (delisted, unlisted, behind a different
329
+ // endpoint). Wholesale replace — same one-shot-save convention as PUT
330
+ // /admin/models/:name. Discovery refreshes only ever rewrite
331
+ // `discoveredModels`, so anything stored here survives every re-scan.
332
+ app.put("/providers/:id/extra-models", async (c) => {
333
+ const id = c.req.param("id");
334
+ const body = await readJson<{ models?: unknown }>(c.req.raw);
335
+ if (!body || !Array.isArray(body.models) || body.models.some((m) => typeof m !== "string")) {
336
+ return c.json({ error: { message: "models (string[]) is required" } }, 400);
337
+ }
338
+ // Canonical form: trimmed, deduped, case-insensitively sorted for scanning.
339
+ const models = [...new Set((body.models as string[]).map((m) => m.trim()).filter(Boolean))].sort((a, b) =>
340
+ a.toLowerCase().localeCompare(b.toLowerCase()),
341
+ );
342
+ let found = false;
343
+ await store.update((d) => {
344
+ const p = d.providers.find((x) => x.id === id);
345
+ if (!p) return;
346
+ found = true;
347
+ if (models.length) p.extraModels = models;
348
+ else delete p.extraModels;
349
+ });
350
+ if (!found) return c.json({ error: { message: "provider not found" } }, 404);
351
+ return c.json({ provider: toPublic(store.get().providers.find((x) => x.id === id)!) });
352
+ });
353
+
326
354
  // Test a SOURCE directly: one minimal ping per selected protocol, straight to
327
355
  // the upstream with this source's own key + base URL. Unlike the model tests
328
356
  // (which loop back through dispatch) this needs no routing config, so an
@@ -31,6 +31,11 @@ export interface Provider {
31
31
  supportsResponses?: boolean;
32
32
  /** Model ids this provider offered at last discovery (cached, may be stale). */
33
33
  discoveredModels?: string[];
34
+ /** Manually supplemented upstream model ids — names a backend's /models list
35
+ * doesn't include but that still work (delisted, unlisted, behind a
36
+ * different endpoint). Merged with `discoveredModels` for editor suggestions
37
+ * and the UI's staleness check; discovery refreshes never touch this list. */
38
+ extraModels?: string[];
34
39
  /** Epoch ms of the last successful/attempted discovery. */
35
40
  discoveredAt?: number;
36
41
  createdAt: number;