From 253199f16f0018c7fe2b1889791178566af19e4f Mon Sep 17 00:00:00 2001 From: Amir Seify Date: Mon, 21 Sep 2026 19:49:13 +0700 Subject: [PATCH] feat(combos): Cursor/Claude Default presets + bulk select/delete/strategy - Add Cursor Default / Claude Default on Dashboard -> Combos to generate unprefixed combo names that match Cursor/Claude client model IDs, seeded with cu/... or cc/... so those clients can route through 9Router. - Add multi-select bulk Delete and bulk Set strategy (Fallback / Round Robin / Fusion). - Docs and unit tests for preset builder. --- .../combo-defaults/cursor-model-list.png | Bin 0 -> 38350 bytes .../override-openai-base-url.png | Bin 0 -> 49400 bytes gitbook/content/en/features/combos.md | 25 ++ src/app/(dashboard)/dashboard/combos/page.js | 352 +++++++++++++++--- src/app/api/combos/presets/route.js | 110 ++++++ src/lib/comboPresets.js | 121 ++++++ tests/unit/combo-presets.test.js | 102 +++++ 7 files changed, 666 insertions(+), 44 deletions(-) create mode 100644 .github/issue-assets/combo-defaults/cursor-model-list.png create mode 100644 .github/issue-assets/combo-defaults/override-openai-base-url.png create mode 100644 src/app/api/combos/presets/route.js create mode 100644 src/lib/comboPresets.js create mode 100644 tests/unit/combo-presets.test.js diff --git a/.github/issue-assets/combo-defaults/cursor-model-list.png b/.github/issue-assets/combo-defaults/cursor-model-list.png new file mode 100644 index 0000000000000000000000000000000000000000..93abfbb1e4ca207905e00aec5ad5f9dcff3c7050 GIT binary patch literal 38350 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 BOxyqf literal 0 HcmV?d00001 diff --git a/.github/issue-assets/combo-defaults/override-openai-base-url.png b/.github/issue-assets/combo-defaults/override-openai-base-url.png new file mode 100644 index 0000000000000000000000000000000000000000..e79238992df0450ffa9d70b3ecfeb8a519263ac1 GIT binary patch literal 49400 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 vhBF`-D? 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 literal 0 HcmV?d00001 diff --git a/gitbook/content/en/features/combos.md b/gitbook/content/en/features/combos.md index 2b94eff5..f91c1f00 100644 --- a/gitbook/content/en/features/combos.md +++ b/gitbook/content/en/features/combos.md @@ -118,6 +118,31 @@ Cursor/Cline/Any tool: --- +## Cursor / Claude Default Combos + +Cursor and Claude Code send **unprefixed** model IDs (`composer-2.5`, `claude-opus-5`, `opus`), while 9Router routes with provider prefixes (`cu/composer-2.5`, `cc/claude-opus-5`). Default combo generators bridge that gap. + +On **Dashboard → Combos**: + +1. Click **Cursor Default** or **Claude Default** +2. Confirm the preview (new vs already-existing names) +3. 9Router creates one combo per client model ID, seeded with the matching prefixed route + +**Examples:** + +| Combo name (what the client sends) | Seeded model (what 9Router routes) | +|------------------------------------|------------------------------------| +| `composer-2.5` | `cu/composer-2.5` | +| `cursor-grok-4.6-high-fast` | `cu/cursor-grok-4.6-high-fast` | +| `claude-opus-5` | `cc/claude-opus-5` | +| `opus` | `cc/claude-opus-5` | + +Existing combo names are **skipped** (not overwritten). Edit any generated combo afterward to add fallbacks. Click the button again later to pick up new catalog IDs. + +> These combos help when Cursor/Claude already talk to 9Router (`/v1` or `ANTHROPIC_BASE_URL`) and send their native model IDs. They do not change Cursor’s built-in Models tab by themselves. + +--- + ## Example Combos ### Example 1: Premium Coding (Subscription → Cheap → Free) diff --git a/src/app/(dashboard)/dashboard/combos/page.js b/src/app/(dashboard)/dashboard/combos/page.js index 52ed7459..c0e20f3e 100644 --- a/src/app/(dashboard)/dashboard/combos/page.js +++ b/src/app/(dashboard)/dashboard/combos/page.js @@ -44,6 +44,12 @@ function normalizeCapEntry(entry) { return { ...EMPTY_CAP_ENTRY }; } +const STRATEGY_OPTIONS = [ + { value: "fallback", label: "Fallback — try in order" }, + { value: "round-robin", label: "Round Robin — rotate" }, + { value: "fusion", label: "Fusion — panel + judge" }, +]; + export default function CombosPage() { const [combos, setCombos] = useState([]); const [loading, setLoading] = useState(true); @@ -52,14 +58,99 @@ export default function CombosPage() { const [activeProviders, setActiveProviders] = useState([]); const [comboStrategies, setComboStrategies] = useState({}); const [capacityAdapter, setCapacityAdapter] = useState(EMPTY_CAPACITY_ADAPTER); - const [confirmState, setConfirmState] = useState(null); const { getCaps } = useModelCaps(); + const [confirmState, setConfirmState] = useState(null); + const [presetLoading, setPresetLoading] = useState(null); // "cursor" | "claude" | null + const [selectedIds, setSelectedIds] = useState([]); + const [bulkBusy, setBulkBusy] = useState(false); const { copied, copy } = useCopyToClipboard(); useEffect(() => { fetchData(); }, []); // eslint-disable-line react-hooks/exhaustive-deps + // Drop stale selection when the combo list changes (delete / refresh). + useEffect(() => { + const alive = new Set(combos.map((c) => c.id)); + setSelectedIds((prev) => prev.filter((id) => alive.has(id))); + }, [combos]); + + const selectedCombos = combos.filter((c) => selectedIds.includes(c.id)); + const allSelected = combos.length > 0 && selectedIds.length === combos.length; + const someSelected = selectedIds.length > 0; + + const toggleSelect = (id) => { + setSelectedIds((prev) => ( + prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id] + )); + }; + + const toggleSelectAll = () => { + setSelectedIds(allSelected ? [] : combos.map((c) => c.id)); + }; + + const clearSelection = () => setSelectedIds([]); + + const handleGeneratePresets = async (source) => { + const label = source === "cursor" ? "Cursor Default" : "Claude Default"; + setPresetLoading(source); + try { + const previewRes = await fetch(`/api/combos/presets?source=${source}`); + const preview = await previewRes.json(); + if (!previewRes.ok) { + alert(preview.error || `Failed to preview ${label}`); + return; + } + + const toCreate = preview.toCreate ?? (preview.items || []).filter((i) => !i.exists).length; + const toSkip = preview.toSkip ?? (preview.items || []).filter((i) => i.exists).length; + const total = (preview.items || []).length; + + if (total === 0) { + alert(`No ${label} models available to generate.`); + return; + } + + if (toCreate === 0) { + alert(`All ${total} ${label} combos already exist. Nothing to create.`); + return; + } + + setConfirmState({ + title: `Generate ${label}`, + message: `Create ${toCreate} combo${toCreate === 1 ? "" : "s"} named like ${source === "cursor" ? "Cursor" : "Claude"} model IDs (seeded with cu/… or cc/…). ${toSkip} already exist and will be skipped. You can edit any combo afterward to add fallbacks.`, + confirmText: "Generate", + variant: "primary", + onConfirm: async () => { + setConfirmState((prev) => prev ? { ...prev, loading: true } : null); + try { + const res = await fetch("/api/combos/presets", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ source }), + }); + const data = await res.json(); + if (!res.ok) { + alert(data.error || `Failed to generate ${label}`); + return; + } + await fetchData(); + setConfirmState(null); + } catch (error) { + console.log(`Error generating ${label}:`, error); + alert(`Failed to generate ${label}`); + setConfirmState((prev) => prev ? { ...prev, loading: false } : null); + } + }, + }); + } catch (error) { + console.log(`Error previewing ${label}:`, error); + alert(`Failed to preview ${label}`); + } finally { + setPresetLoading(null); + } + }; + const fetchData = async () => { try { const [combosRes, providersRes, settingsRes] = await Promise.all([ @@ -141,24 +232,80 @@ export default function CombosPage() { } }; + const pruneStrategiesForNames = (names, base = comboStrategies) => { + const updated = { ...base }; + for (const name of names) delete updated[name]; + return updated; + }; + + const persistComboStrategies = async (updated) => { + await fetch("/api/settings", { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ comboStrategies: updated }), + }); + setComboStrategies(updated); + }; + const handleDelete = async (id) => { + const combo = combos.find((c) => c.id === id); setConfirmState({ title: "Delete Combo", - message: "Delete this combo?", + message: combo ? `Delete combo "${combo.name}"?` : "Delete this combo?", onConfirm: async () => { - setConfirmState(null); + setConfirmState((prev) => prev ? { ...prev, loading: true } : null); try { const res = await fetch(`/api/combos/${id}`, { method: "DELETE" }); if (res.ok) { - setCombos(combos.filter(c => c.id !== id)); + if (combo?.name) { + await persistComboStrategies(pruneStrategiesForNames([combo.name])); + } + setCombos((prev) => prev.filter((c) => c.id !== id)); + setSelectedIds((prev) => prev.filter((x) => x !== id)); } + setConfirmState(null); } catch (error) { console.log("Error deleting combo:", error); + setConfirmState((prev) => prev ? { ...prev, loading: false } : null); } } }); }; + const handleBulkDelete = () => { + if (selectedCombos.length === 0) return; + const count = selectedCombos.length; + setConfirmState({ + title: "Delete Selected Combos", + message: `Delete ${count} selected combo${count === 1 ? "" : "s"}? This cannot be undone.`, + confirmText: "Delete", + variant: "danger", + onConfirm: async () => { + setConfirmState((prev) => prev ? { ...prev, loading: true } : null); + setBulkBusy(true); + try { + const ids = selectedCombos.map((c) => c.id); + const names = selectedCombos.map((c) => c.name); + const results = await Promise.all( + ids.map((id) => fetch(`/api/combos/${id}`, { method: "DELETE" })) + ); + const failed = results.filter((r) => !r.ok).length; + await persistComboStrategies(pruneStrategiesForNames(names)); + setCombos((prev) => prev.filter((c) => !ids.includes(c.id))); + clearSelection(); + setConfirmState(null); + if (failed > 0) alert(`Deleted with ${failed} failure${failed === 1 ? "" : "s"}.`); + } catch (error) { + console.log("Error bulk deleting combos:", error); + alert("Failed to delete selected combos"); + setConfirmState((prev) => prev ? { ...prev, loading: false } : null); + } finally { + setBulkBusy(false); + } + }, + }); + }; + // Merge a per-combo strategy patch into settings.comboStrategies. Passing an empty // patch (strategy back to default "fallback") drops the entry entirely. const handleSetComboStrategy = async (comboName, patch) => { @@ -172,18 +319,36 @@ export default function CombosPage() { updated[comboName] = next; } - await fetch("/api/settings", { - method: "PATCH", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ comboStrategies: updated }), - }); - - setComboStrategies(updated); + await persistComboStrategies(updated); } catch (error) { console.log("Error updating combo strategy:", error); } }; + const handleBulkSetStrategy = async (strategy) => { + if (selectedCombos.length === 0 || !strategy) return; + setBulkBusy(true); + try { + const updated = { ...comboStrategies }; + for (const combo of selectedCombos) { + if (!strategy || strategy === "fallback") { + delete updated[combo.name]; + } else { + updated[combo.name] = { + ...(updated[combo.name] || {}), + fallbackStrategy: strategy, + }; + } + } + await persistComboStrategies(updated); + } catch (error) { + console.log("Error bulk updating combo strategy:", error); + alert("Failed to update strategy for selected combos"); + } finally { + setBulkBusy(false); + } + }; + if (loading) { return (
@@ -196,7 +361,7 @@ export default function CombosPage() { return (
{/* Header */} -
+

Group models under one name, then pick a strategy per combo: @@ -206,10 +371,40 @@ export default function CombosPage() {

  • Round Robin — rotates models across requests to spread load
  • Fusion — queries all models in parallel, then a judge synthesizes one answer. Best quality, but costs the most: every request bills all panel models + the judge (N+1 calls)
  • +

    + Cursor / Claude Default create combos named exactly like those clients' model IDs (e.g. composer-2.5, opus), seeded with the matching cu/… or cc/… route so traffic can hit 9router without the prefix. + {" "}Note: Cursor IDE itself often blocks built-in Composer / Grok from Override OpenAI Base URL ("model does not support custom API"); add them via Cursor's Add Custom Model using the combo name, or pick a model Cursor allows through the custom endpoint. +

    +
    +
    + +
    + + +
    -
    {/* Combos List */} @@ -227,25 +422,88 @@ export default function CombosPage() {
    ) : ( -
    - {(() => { - const comboByName = Object.fromEntries(combos.map((c) => [c.name, c.models])); - return combos.map((combo) => ( - setEditingCombo(combo)} - onDelete={() => handleDelete(combo.id)} - strategy={comboStrategies[combo.name] || {}} - onSetStrategy={(patch) => handleSetComboStrategy(combo.name, patch)} +
    + {/* Selection toolbar */} +
    + + +
    + {someSelected && ( + <> +
    + e.stopPropagation()} + className="h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary" + aria-label={`Select ${combo.name}`} + /> +
    layers
    diff --git a/src/app/api/combos/presets/route.js b/src/app/api/combos/presets/route.js new file mode 100644 index 00000000..58d0541b --- /dev/null +++ b/src/app/api/combos/presets/route.js @@ -0,0 +1,110 @@ +import { NextResponse } from "next/server"; +import { getCombos, createCombo, getProviderConnections } from "@/lib/localDb"; +import { buildPresetItems, PRESET_SOURCES } from "@/lib/comboPresets"; +import { resolveCursorModels } from "open-sse/services/cursorModels.js"; + +export const dynamic = "force-dynamic"; + +/** + * Resolve live Cursor catalog from the first active cursor connection, if any. + * @returns {Promise|null>} + */ +async function fetchCursorLiveModels() { + try { + const connections = await getProviderConnections(); + const conn = (connections || []).find( + (c) => c.provider === "cursor" && c.isActive !== false + ); + if (!conn) return null; + const result = await resolveCursorModels({ + accessToken: conn.accessToken, + providerSpecificData: conn.providerSpecificData || {}, + }, { log: console }); + return result?.models?.length ? result.models : null; + } catch (error) { + console.log("combo presets: cursor live catalog failed", error?.message || error); + return null; + } +} + +/** + * @param {string} source + * @returns {Promise<{ name: string, models: string[], exists: boolean }[]>} + */ +async function resolvePresetItems(source) { + const combos = await getCombos(); + const existingNames = (combos || []).map((c) => c.name); + const liveModels = source === "cursor" ? await fetchCursorLiveModels() : null; + return buildPresetItems(source, { + liveModels: liveModels || undefined, + existingNames, + }); +} + +function parseSource(value) { + if (!value || !PRESET_SOURCES.has(value)) return null; + return value; +} + +// GET /api/combos/presets?source=cursor|claude — preview items +export async function GET(request) { + try { + const { searchParams } = new URL(request.url); + const source = parseSource(searchParams.get("source")); + if (!source) { + return NextResponse.json( + { error: "source must be 'cursor' or 'claude'" }, + { status: 400 } + ); + } + + const items = await resolvePresetItems(source); + return NextResponse.json({ + source, + items, + toCreate: items.filter((i) => !i.exists).length, + toSkip: items.filter((i) => i.exists).length, + }); + } catch (error) { + console.log("Error previewing combo presets:", error); + return NextResponse.json({ error: "Failed to preview combo presets" }, { status: 500 }); + } +} + +// POST /api/combos/presets — create missing combos for a source +export async function POST(request) { + try { + const body = await request.json().catch(() => ({})); + const source = parseSource(body?.source); + if (!source) { + return NextResponse.json( + { error: "source must be 'cursor' or 'claude'" }, + { status: 400 } + ); + } + + const items = await resolvePresetItems(source); + const created = []; + const skipped = []; + + for (const item of items) { + if (item.exists) { + skipped.push(item.name); + continue; + } + const combo = await createCombo({ name: item.name, models: item.models }); + created.push(combo); + } + + return NextResponse.json({ + source, + created, + skipped, + createdCount: created.length, + skippedCount: skipped.length, + }); + } catch (error) { + console.log("Error creating combo presets:", error); + return NextResponse.json({ error: "Failed to create combo presets" }, { status: 500 }); + } +} diff --git a/src/lib/comboPresets.js b/src/lib/comboPresets.js new file mode 100644 index 00000000..f45ae058 --- /dev/null +++ b/src/lib/comboPresets.js @@ -0,0 +1,121 @@ +/** + * Build Cursor / Claude default combo presets. + * Combo names match client-native model IDs (no provider prefix); + * each is seeded with the matching prefixed 9router model so routing works. + */ + +import { getProviderModels } from "open-sse/config/providerModels.js"; +import { CLI_TOOLS } from "@/shared/constants/cliTools"; + +export const VALID_COMBO_NAME_REGEX = /^[a-zA-Z0-9_.\-]+$/; +export const PRESET_SOURCES = new Set(["cursor", "claude"]); + +const CURSOR_ALIAS = "cu"; +const CLAUDE_ALIAS = "cc"; + +/** Extra Claude Code aliases not listed in defaultModels. */ +const CLAUDE_EXTRA_ALIAS_TARGETS = { + default: "cc/claude-sonnet-5", + opusplan: "cc/claude-opus-5", +}; + +/** + * @param {string} name + * @returns {boolean} + */ +export function isValidComboPresetName(name) { + return typeof name === "string" && name.length > 0 && VALID_COMBO_NAME_REGEX.test(name); +} + +/** + * @param {string} name + * @param {string[]} models + * @param {Set} [seen] + * @returns {{ name: string, models: string[] }|null} + */ +function pushItem(name, models, seen) { + if (!isValidComboPresetName(name)) return null; + if (seen?.has(name)) return null; + if (!Array.isArray(models) || models.length === 0) return null; + seen?.add(name); + return { name, models }; +} + +/** + * @param {Array<{id?: string}|string>} modelList + * @param {string} providerAlias + * @param {Set} seen + * @returns {{ name: string, models: string[] }[]} + */ +function itemsFromProviderModels(modelList, providerAlias, seen) { + const out = []; + for (const entry of modelList || []) { + const id = typeof entry === "string" ? entry : entry?.id; + if (!id) continue; + const item = pushItem(id, [`${providerAlias}/${id}`], seen); + if (item) out.push(item); + } + return out; +} + +/** + * Build Cursor default presets. + * Prefer live catalog when provided; otherwise static cu registry. + * @param {{ liveModels?: Array<{id: string}|string> }} [opts] + * @returns {{ name: string, models: string[] }[]} + */ +export function buildCursorPresetItems(opts = {}) { + const seen = new Set(); + const live = Array.isArray(opts.liveModels) ? opts.liveModels : null; + if (live?.length) { + return itemsFromProviderModels(live, CURSOR_ALIAS, seen); + } + return itemsFromProviderModels(getProviderModels(CURSOR_ALIAS), CURSOR_ALIAS, seen); +} + +/** + * Build Claude default presets from cc registry + Claude Code aliases. + * @returns {{ name: string, models: string[] }[]} + */ +export function buildClaudePresetItems() { + const seen = new Set(); + const out = itemsFromProviderModels(getProviderModels(CLAUDE_ALIAS), CLAUDE_ALIAS, seen); + + const claudeTool = CLI_TOOLS.claude || {}; + for (const entry of claudeTool.defaultModels || []) { + const name = entry.alias || entry.id; + const target = entry.defaultValue; + if (!name || !target) continue; + const item = pushItem(name, [target], seen); + if (item) out.push(item); + } + + for (const alias of claudeTool.modelAliases || []) { + if (seen.has(alias)) continue; + const target = CLAUDE_EXTRA_ALIAS_TARGETS[alias]; + if (!target) continue; + const item = pushItem(alias, [target], seen); + if (item) out.push(item); + } + + return out; +} + +/** + * @param {"cursor"|"claude"} source + * @param {{ liveModels?: Array<{id: string}|string>, existingNames?: Iterable }} [opts] + * @returns {{ name: string, models: string[], exists: boolean }[]} + */ +export function buildPresetItems(source, opts = {}) { + if (!PRESET_SOURCES.has(source)) return []; + + const items = source === "cursor" + ? buildCursorPresetItems({ liveModels: opts.liveModels }) + : buildClaudePresetItems(); + + const existing = new Set(opts.existingNames || []); + return items.map((item) => ({ + ...item, + exists: existing.has(item.name), + })); +} diff --git a/tests/unit/combo-presets.test.js b/tests/unit/combo-presets.test.js new file mode 100644 index 00000000..e5a06554 --- /dev/null +++ b/tests/unit/combo-presets.test.js @@ -0,0 +1,102 @@ +import { describe, it, expect } from "vitest"; +import { + buildPresetItems, + buildCursorPresetItems, + buildClaudePresetItems, + isValidComboPresetName, +} from "../../src/lib/comboPresets.js"; + +describe("combo presets", () => { + it("rejects combo names with slashes or invalid chars", () => { + expect(isValidComboPresetName("composer-2.5")).toBe(true); + expect(isValidComboPresetName("claude-opus-5")).toBe(true); + expect(isValidComboPresetName("cu/composer-2.5")).toBe(false); + expect(isValidComboPresetName("bad name")).toBe(false); + expect(isValidComboPresetName("")).toBe(false); + }); + + it("Cursor live ids become unprefixed names seeded with cu/…", () => { + const items = buildCursorPresetItems({ + liveModels: [ + { id: "composer-2.5", name: "Composer 2.5" }, + { id: "cursor-grok-4.6-high-fast", name: "Grok" }, + { id: "bad/with-slash", name: "Invalid" }, + ], + }); + + expect(items).toEqual([ + { name: "composer-2.5", models: ["cu/composer-2.5"] }, + { name: "cursor-grok-4.6-high-fast", models: ["cu/cursor-grok-4.6-high-fast"] }, + ]); + }); + + it("Cursor falls back to static cu registry when live catalog is empty", () => { + const items = buildCursorPresetItems({ liveModels: [] }); + expect(items.length).toBeGreaterThan(0); + expect(items.every((i) => i.models[0].startsWith("cu/"))).toBe(true); + expect(items.some((i) => i.name === "default")).toBe(true); + // No slash in combo name + expect(items.every((i) => !i.name.includes("/"))).toBe(true); + }); + + it("Claude aliases map opus → cc/claude-opus-5 and registry models seed cc/…", () => { + const items = buildClaudePresetItems(); + const byName = Object.fromEntries(items.map((i) => [i.name, i])); + + expect(byName["claude-opus-5"]).toEqual({ + name: "claude-opus-5", + models: ["cc/claude-opus-5"], + }); + expect(byName.opus).toEqual({ + name: "opus", + models: ["cc/claude-opus-5"], + }); + expect(byName.sonnet).toEqual({ + name: "sonnet", + models: ["cc/claude-sonnet-5"], + }); + expect(byName.haiku).toEqual({ + name: "haiku", + models: ["cc/claude-haiku-4-5-20251001"], + }); + expect(byName.fable).toEqual({ + name: "fable", + models: ["cc/claude-fable-5"], + }); + expect(byName.default).toEqual({ + name: "default", + models: ["cc/claude-sonnet-5"], + }); + expect(byName.opusplan).toEqual({ + name: "opusplan", + models: ["cc/claude-opus-5"], + }); + }); + + it("marks existing names with exists: true", () => { + const items = buildPresetItems("cursor", { + liveModels: [ + { id: "composer-2.5" }, + { id: "gpt-5.3-codex" }, + ], + existingNames: ["composer-2.5"], + }); + + expect(items).toEqual([ + { name: "composer-2.5", models: ["cu/composer-2.5"], exists: true }, + { name: "gpt-5.3-codex", models: ["cu/gpt-5.3-codex"], exists: false }, + ]); + }); + + it("returns empty for unknown source", () => { + expect(buildPresetItems("unknown")).toEqual([]); + }); + + it("drops invalid names from Claude/Cursor catalogs", () => { + const cursor = buildPresetItems("cursor", { + liveModels: [{ id: "ok-model" }, { id: "no/slash" }, { id: "has space" }], + existingNames: [], + }); + expect(cursor.map((i) => i.name)).toEqual(["ok-model"]); + }); +});