amicus 1.0.0 → 1.1.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.
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Quick-pick resolution (wizard Step 2) — resolves each curated family to
3
+ * the current catalog flagship. Setup-time only; runtime alias resolution
4
+ * stays on the static DEFAULT_ALIASES (see curated-models.js).
5
+ *
6
+ * Ranking: numeric-descending over ids; a marker-suffixed variant
7
+ * (-preview/-exp/-beta/-latest/:free) loses ONLY to its own unmarked base,
8
+ * so gemini-3.1-pro-preview still beats the older stable gemini-2.5-pro.
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const { getFamilies, toDefaultAliases } = require('./curated-models');
14
+
15
+ const MARKER_RE = /(-preview|-exp|-beta|-latest|:free)+$/;
16
+
17
+ /** Numeric-desc comparator; same-base marker variant sorts after its base. */
18
+ function compareIdsDesc(a, b) {
19
+ const aBase = a.replace(MARKER_RE, '');
20
+ const bBase = b.replace(MARKER_RE, '');
21
+ if (aBase === bBase && a !== b) {
22
+ if (a === aBase) { return -1; }
23
+ if (b === bBase) { return 1; }
24
+ return a.localeCompare(b, 'en', { numeric: true });
25
+ }
26
+ return b.localeCompare(a, 'en', { numeric: true });
27
+ }
28
+
29
+ /**
30
+ * Newest catalog id under `<nsPrefix><vendorPath>/` whose model segment
31
+ * matches idPattern. nsPrefix is 'openrouter/' or '' (direct rows).
32
+ * @param {string} vendorPath - vendor segment in the OpenRouter namespace,
33
+ * or the provider name when matching direct-namespace rows (nsPrefix '').
34
+ * @returns {string|null} full catalog id
35
+ */
36
+ function pickCurrent(catalog, nsPrefix, vendorPath, idPattern) {
37
+ const prefix = `${nsPrefix}${vendorPath}/`;
38
+ const ids = (Array.isArray(catalog) ? catalog : [])
39
+ .map(m => m && m.id)
40
+ .filter(id => typeof id === 'string' && id.startsWith(prefix))
41
+ .filter(id => idPattern.test(id.slice(prefix.length)));
42
+ if (ids.length === 0) { return null; }
43
+ return ids.sort(compareIdsDesc)[0];
44
+ }
45
+
46
+ /**
47
+ * @param {Array<{id:string}>} catalog
48
+ * @returns {Array<{alias,label,blurb,source:'live'|'fallback',routes:Object<string,string>}>}
49
+ * `routes` may be empty if a family defines no fallback and the catalog has no match.
50
+ */
51
+ function resolveQuickPicks(catalog) {
52
+ return getFamilies().map(f => {
53
+ const routes = {};
54
+ let live = false;
55
+ const orPick = pickCurrent(catalog, 'openrouter/', f.vendorPath, f.idPattern);
56
+ if (orPick) { routes.openrouter = orPick; live = true; }
57
+ else if (f.fallback.openrouter) { routes.openrouter = f.fallback.openrouter; }
58
+ for (const p of f.directProviders) {
59
+ const direct = pickCurrent(catalog, '', p, f.idPattern);
60
+ if (direct) { routes[p] = direct; live = true; }
61
+ else if (f.fallback[p]) { routes[p] = f.fallback[p]; }
62
+ }
63
+ return { alias: f.alias, label: f.label, blurb: f.blurb, routes,
64
+ source: live ? 'live' : 'fallback' };
65
+ });
66
+ }
67
+
68
+ /**
69
+ * Seed map for fresh configs: static defaults overlaid with live family
70
+ * openrouter routes (cardless aliases stay pinned).
71
+ * @returns {Object<string,string>}
72
+ */
73
+ function toLiveSeedAliases(catalog) {
74
+ const seeds = toDefaultAliases();
75
+ for (const r of resolveQuickPicks(catalog || [])) {
76
+ if (r.source === 'live' && r.routes.openrouter) { seeds[r.alias] = r.routes.openrouter; }
77
+ }
78
+ return seeds;
79
+ }
80
+
81
+ module.exports = { compareIdsDesc, pickCurrent, resolveQuickPicks, toLiveSeedAliases };