diff --git a/@omniroute/opencode-plugin/src/index.ts b/@omniroute/opencode-plugin/src/index.ts index 19dca23be2..6aae7a8238 100644 --- a/@omniroute/opencode-plugin/src/index.ts +++ b/@omniroute/opencode-plugin/src/index.ts @@ -240,10 +240,18 @@ export function resolveOmniRoutePluginOptions( opts?: OmniRoutePluginOptions ): Required> & Pick { - const providerId = opts?.providerId ?? OMNIROUTE_PROVIDER_KEY; + const rawProviderId = opts?.providerId ?? OMNIROUTE_PROVIDER_KEY; + // OC 1.17.8+ native-adapter gate rejects providerID not in + // {openai, anthropic, opencode*}. Silently prefix so existing + // configs (providerId: "omniroute") keep working. + const providerId = rawProviderId.startsWith("opencode-") + ? rawProviderId + : `opencode-${rawProviderId}`; const displayName = opts?.displayName ?? - (providerId === OMNIROUTE_PROVIDER_KEY ? "OmniRoute" : `OmniRoute (${providerId})`); + (providerId === `opencode-${OMNIROUTE_PROVIDER_KEY}` + ? "OmniRoute" + : `OmniRoute (${providerId})`); const modelCacheTtl = typeof opts?.modelCacheTtl === "number" && opts.modelCacheTtl > 0 ? opts.modelCacheTtl diff --git a/@omniroute/opencode-plugin/tests/auth.test.ts b/@omniroute/opencode-plugin/tests/auth.test.ts index 76c071a61c..f393f0470f 100644 --- a/@omniroute/opencode-plugin/tests/auth.test.ts +++ b/@omniroute/opencode-plugin/tests/auth.test.ts @@ -13,12 +13,12 @@ import { createOmniRouteAuthHook } from "../src/index.js"; test("createOmniRouteAuthHook: default providerId is 'omniroute'", () => { const hook = createOmniRouteAuthHook(); - assert.equal(hook.provider, "omniroute"); + assert.equal(hook.provider, "opencode-omniroute"); }); test("createOmniRouteAuthHook: custom providerId binds to hook.provider (multi-instance)", () => { const hook = createOmniRouteAuthHook({ providerId: "omniroute-preprod" }); - assert.equal(hook.provider, "omniroute-preprod"); + assert.equal(hook.provider, "opencode-omniroute-preprod"); }); test("createOmniRouteAuthHook: methods[0] is type 'api' with label including displayName", () => { @@ -30,7 +30,7 @@ test("createOmniRouteAuthHook: methods[0] is type 'api' with label including dis assert.equal(m.label, "OmniRoute API Key"); const custom = createOmniRouteAuthHook({ providerId: "omniroute-preprod" }); - assert.equal(custom.methods[0].label, "OmniRoute (omniroute-preprod) API Key"); + assert.equal(custom.methods[0].label, "OmniRoute (opencode-omniroute-preprod) API Key"); }); test("createOmniRouteAuthHook: prompts[0] uses key='apiKey' per @opencode-ai/plugin contract", () => { diff --git a/@omniroute/opencode-plugin/tests/combos.test.ts b/@omniroute/opencode-plugin/tests/combos.test.ts index ff209a9a67..04102c0055 100644 --- a/@omniroute/opencode-plugin/tests/combos.test.ts +++ b/@omniroute/opencode-plugin/tests/combos.test.ts @@ -447,14 +447,14 @@ test("models() returns combo entries merged into the map", async () => { // 3 raw models + 1 combo = 4 entries assert.equal(Object.keys(out).length, 4); - assert.ok(out["omniroute/claude-primary"]); - assert.ok(out["omniroute/claude-secondary"]); - assert.ok(out["omniroute/gemini-3-flash"]); - assert.ok(out["omniroute/claude-tier"]); + assert.ok(out["opencode-omniroute/claude-primary"]); + assert.ok(out["opencode-omniroute/claude-secondary"]); + assert.ok(out["opencode-omniroute/gemini-3-flash"]); + assert.ok(out["opencode-omniroute/claude-tier"]); - const combo = out["omniroute/claude-tier"]; + const combo = out["opencode-omniroute/claude-tier"]; assert.equal(combo.name, "Claude Tier"); - assert.equal(combo.providerID, "omniroute"); + assert.equal(combo.providerID, "opencode-omniroute"); // LCD over claude-primary (200k, reasoning) + claude-secondary (100k, no reasoning) assert.equal(combo.limit.context, 100_000); assert.equal(combo.capabilities.reasoning, false); @@ -478,11 +478,11 @@ test("models(): combo with unknown member ids degrades to all-false LCD posture" { fetcher: modelsFetcher, combosFetcher } ); const out = await hook.models!({} as never, { auth: apiAuth("sk-z") as never }); - assert.ok(out["omniroute/phantom-combo"]); + assert.ok(out["opencode-omniroute/phantom-combo"]); // With zero resolvable members, LCD = all-false (defensive posture). - assert.equal(out["omniroute/phantom-combo"].capabilities.toolcall, false); - assert.equal(out["omniroute/phantom-combo"].capabilities.reasoning, false); - assert.equal(out["omniroute/phantom-combo"].limit.context, 0); + assert.equal(out["opencode-omniroute/phantom-combo"].capabilities.toolcall, false); + assert.equal(out["opencode-omniroute/phantom-combo"].capabilities.reasoning, false); + assert.equal(out["opencode-omniroute/phantom-combo"].limit.context, 0); }); test("models(): hidden combos are excluded from the map", async () => { @@ -505,8 +505,8 @@ test("models(): hidden combos are excluded from the map", async () => { { fetcher: modelsFetcher, combosFetcher } ); const out = await hook.models!({} as never, { auth: apiAuth("sk-z") as never }); - assert.ok(out["omniroute/visible"]); - assert.ok(!out["omniroute/hidden"], "hidden combo must be omitted"); + assert.ok(out["opencode-omniroute/visible"]); + assert.ok(!out["opencode-omniroute/hidden"], "hidden combo must be omitted"); }); test("models(): combo name exactly matches raw model id → raw deleted, raw deleted, no warn", async () => { @@ -530,8 +530,8 @@ test("models(): combo name exactly matches raw model id → raw deleted, raw del }); // Raw model replaced by combo of the same key; combo now lives at the bare slug. - assert.ok(out["omniroute/claude-primary"], "combo surfaces under prefixed key"); - assert.equal(out["omniroute/claude-primary"].name, "claude-primary"); + assert.ok(out["opencode-omniroute/claude-primary"], "combo surfaces under prefixed key"); + assert.equal(out["opencode-omniroute/claude-primary"].name, "claude-primary"); // No collision warning fires — dedup makes keys disjoint. const collisionWarns = warnings.filter((w) => { @@ -565,8 +565,8 @@ test("models(): two combos with same slug → second gets disambiguator suffix", const out = await hook.models!({} as never, { auth: apiAuth("sk-z") as never }); // First combo gets the bare slug; second gets disambiguated. - assert.ok(out["omniroute/claude"], "first combo at prefixed slug"); - assert.ok(out["omniroute/claude-uuid"], "second combo disambiguated by id prefix"); + assert.ok(out["opencode-omniroute/claude"], "first combo at prefixed slug"); + assert.ok(out["opencode-omniroute/claude-uuid"], "second combo disambiguated by id prefix"); }); test("models(): combos fetch fails → falls back to models-only, warn emitted, no throw", async () => { @@ -583,8 +583,8 @@ test("models(): combos fetch fails → falls back to models-only, warn emitted, // Catalog includes the models but NOT any combo entries. assert.equal(Object.keys(out).length, 2); - assert.ok(out["omniroute/claude-primary"]); - assert.ok(out["omniroute/claude-secondary"]); + assert.ok(out["opencode-omniroute/claude-primary"]); + assert.ok(out["opencode-omniroute/claude-secondary"]); // Soft-fail warning surfaced. const softFail = warnings.find((w) => { @@ -609,7 +609,7 @@ test("models(): combos cached + reused within TTL (one combo fetch per TTL windo const second = await hook.models!({} as never, { auth: apiAuth("sk-z") as never }); assert.equal(combosFetcher.callCount(), 1, "combos fetched only once within TTL"); assert.equal(modelsFetcher.callCount(), 1, "models fetched only once within TTL"); - assert.ok(second["omniroute/claude-tier"]); + assert.ok(second["opencode-omniroute/claude-tier"]); }); test("models(): combos refetched after TTL expiry (same key as models)", async () => { @@ -701,7 +701,7 @@ test("models(): nested combo-ref context is the min of nested + raw members", as { fetcher: modelsFetcher, combosFetcher } ); const out = await hook.models!({} as never, { auth: apiAuth("sk-z") as never }); - const masterLight = out["omniroute/master-light"]; + const masterLight = out["opencode-omniroute/master-light"]; assert.ok(masterLight, "MASTER-LIGHT entry must exist"); assert.equal( masterLight.limit.context, diff --git a/@omniroute/opencode-plugin/tests/config-shim.test.ts b/@omniroute/opencode-plugin/tests/config-shim.test.ts index 11dc76c032..16cb3253cf 100644 --- a/@omniroute/opencode-plugin/tests/config-shim.test.ts +++ b/@omniroute/opencode-plugin/tests/config-shim.test.ts @@ -203,7 +203,7 @@ function makeInput(initialProvider: Record = {}): Config { test("config: with valid auth.json + apiKey + baseURL → mutates input.provider[id] with stripped models block", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-test-1", baseURL: "https://or.example.com/v1" }, + "opencode-omniroute": { type: "api", key: "sk-test-1", baseURL: "https://or.example.com/v1" }, }); const fetcher = stubModelsFetcher([MODEL_CLAUDE, MODEL_GEMINI]); const combosFetcher = stubCombosFetcher([COMBO_CLAUDE_TIER]); @@ -217,8 +217,8 @@ test("config: with valid auth.json + apiKey + baseURL → mutates input.provider await hook(input); const provider = (input as { provider: Record }).provider; - const entry = provider.omniroute; - assert.ok(entry, "input.provider.omniroute set"); + const entry = provider["opencode-omniroute"]; + assert.ok(entry, "input.provider['opencode-omniroute'] set"); assert.equal(entry.npm, "@ai-sdk/openai-compatible"); assert.equal(entry.name, "OmniRoute"); assert.equal(entry.options.baseURL, "https://or.example.com/v1"); @@ -227,7 +227,7 @@ test("config: with valid auth.json + apiKey + baseURL → mutates input.provider // Stripped per-model shape: name + cap flags + modalities + (optional) // cost. OC's SDK static schema accepts only `limit.{context,output}` — // `limit.input` is NOT in the SDK shape and gets dropped silently. - const claude = entry.models["omniroute/claude-sonnet-4-6"]; + const claude = entry.models["opencode-omniroute/claude-sonnet-4-6"]; assert.ok(claude, "claude model surfaced"); assert.equal(claude.name, "claude-sonnet-4-6"); assert.equal(claude.attachment, true); @@ -248,7 +248,7 @@ test("config: with valid auth.json + apiKey + baseURL → mutates input.provider // Combo surfaces under bare key + LCD'd // (gemini's reasoning=false → combo reasoning=false). - const combo = entry.models["omniroute/claude-tier"]; + const combo = entry.models["opencode-omniroute/claude-tier"]; assert.ok(combo, "combo surfaced under bare key"); assert.equal(combo.name, "Claude Tier"); assert.equal(combo.reasoning, false, "LCD: any member reasoning=false → combo reasoning=false"); @@ -323,7 +323,7 @@ test("config: existing input.provider[id] → no overwrite (respect manual overr models: { "manual-model": { name: "manual-model" } }, }; const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, }); const fetcher = stubModelsFetcher([MODEL_CLAUDE]); const combosFetcher = stubCombosFetcher([]); @@ -333,11 +333,11 @@ test("config: existing input.provider[id] → no overwrite (respect manual overr { providerId: "omniroute" }, { readAuthJson, fetcher, combosFetcher, logger } ); - const input = makeInput({ omniroute: manual }); + const input = makeInput({ "opencode-omniroute": manual }); await hook(input); const provider = (input as { provider: Record }).provider; - assert.equal(provider.omniroute, manual, "manual override preserved by reference"); + assert.equal(provider["opencode-omniroute"], manual, "manual override preserved by reference"); assert.equal(fetcher.callCount(), 0, "no fetch — short-circuited before I/O"); assert.equal(readAuthJson.callCount(), 0, "no auth.json read either"); assert.ok( @@ -352,7 +352,7 @@ test("config: existing input.provider[id] → no overwrite (respect manual overr test("config: fetchers throw → warn + emit stub entry with models: {}", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, }); const fetcher = throwingModelsFetcher(); const combosFetcher = throwingCombosFetcher(); @@ -368,8 +368,9 @@ test("config: fetchers throw → warn + emit stub entry with models: {}", async const input = makeInput(); await hook(input); - const entry = (input as { provider: Record }).provider - .omniroute; + const entry = (input as { provider: Record }).provider[ + "opencode-omniroute" + ]; assert.ok(entry, "stub provider entry published even when fetchers fail"); assert.equal(entry.npm, "@ai-sdk/openai-compatible"); assert.deepEqual(entry.models, {}, "models stub is empty object"); @@ -392,7 +393,7 @@ test("config: fetchers throw → warn + emit stub entry with models: {}", async test("config: combos fetcher throws → emit models-only catalog (no combos in models block)", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, }); const fetcher = stubModelsFetcher([MODEL_CLAUDE, MODEL_GEMINI]); const combosFetcher = throwingCombosFetcher(); @@ -405,12 +406,16 @@ test("config: combos fetcher throws → emit models-only catalog (no combos in m const input = makeInput(); await hook(input); - const entry = (input as { provider: Record }).provider - .omniroute; + const entry = (input as { provider: Record }).provider[ + "opencode-omniroute" + ]; assert.ok(entry); const ids = Object.keys(entry.models).sort(); - assert.deepEqual(ids, ["omniroute/claude-sonnet-4-6", "omniroute/gemini-3-flash"]); - assert.equal(entry.models["omniroute/claude-tier"], undefined, "no combo entry"); + assert.deepEqual(ids, [ + "opencode-omniroute/claude-sonnet-4-6", + "opencode-omniroute/gemini-3-flash", + ]); + assert.equal(entry.models["opencode-omniroute/claude-tier"], undefined, "no combo entry"); assert.ok( logger.entries.some((e) => String(e[0]).includes("/api/combos fetch failed")), "combos-fetch breadcrumb emitted" @@ -423,7 +428,7 @@ test("config: combos fetcher throws → emit models-only catalog (no combos in m test("config: baseURL from auth.json takes precedence when opts.baseURL absent", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-test", baseURL: "https://creds.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://creds.example/v1" }, }); const fetcher = stubModelsFetcher([MODEL_CLAUDE]); const combosFetcher = stubCombosFetcher([]); @@ -437,14 +442,15 @@ test("config: baseURL from auth.json takes precedence when opts.baseURL absent", await hook(input); assert.equal(fetcher.callsBy()[0][0], "https://creds.example/v1"); - const entry = (input as { provider: Record }).provider - .omniroute; + const entry = (input as { provider: Record }).provider[ + "opencode-omniroute" + ]; assert.equal(entry.options.baseURL, "https://creds.example/v1"); }); test("config: opts.baseURL wins over auth.json's stored baseURL", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-test", baseURL: "https://creds.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://creds.example/v1" }, }); const fetcher = stubModelsFetcher([MODEL_CLAUDE]); const combosFetcher = stubCombosFetcher([]); @@ -458,14 +464,15 @@ test("config: opts.baseURL wins over auth.json's stored baseURL", async () => { await hook(input); assert.equal(fetcher.callsBy()[0][0], "https://opts.example/v1"); - const entry = (input as { provider: Record }).provider - .omniroute; + const entry = (input as { provider: Record }).provider[ + "opencode-omniroute" + ]; assert.equal(entry.options.baseURL, "https://opts.example/v1"); }); test("config: no baseURL resolvable (no opts, no auth.json baseURL) → no-op", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-test" }, // NO baseURL on the credential + "opencode-omniroute": { type: "api", key: "sk-test" }, // NO baseURL on the credential }); const fetcher = stubModelsFetcher([MODEL_CLAUDE]); const combosFetcher = stubCombosFetcher([]); @@ -493,12 +500,12 @@ test("config: no baseURL resolvable (no opts, no auth.json baseURL) → no-op", test("config: multi-instance — two plugins with different providerIds publish to their own keys without collision", async () => { const readAuthJson = stubReadAuthJson({ - "omniroute-prod": { + "opencode-omniroute-prod": { type: "api", key: "sk-prod", baseURL: "https://prod.example/v1", }, - "omniroute-preprod": { + "opencode-omniroute-preprod": { type: "api", key: "sk-preprod", baseURL: "https://preprod.example/v1", @@ -522,15 +529,18 @@ test("config: multi-instance — two plugins with different providerIds publish await hookB(input); const provider = (input as { provider: Record }).provider; - assert.ok(provider["omniroute-prod"], "prod block present"); - assert.ok(provider["omniroute-preprod"], "preprod block present"); - assert.equal(provider["omniroute-prod"].options.apiKey, "sk-prod"); - assert.equal(provider["omniroute-preprod"].options.apiKey, "sk-preprod"); - assert.equal(provider["omniroute-prod"].options.baseURL, "https://prod.example/v1"); - assert.equal(provider["omniroute-preprod"].options.baseURL, "https://preprod.example/v1"); + assert.ok(provider["opencode-omniroute-prod"], "prod block present"); + assert.ok(provider["opencode-omniroute-preprod"], "preprod block present"); + assert.equal(provider["opencode-omniroute-prod"].options.apiKey, "sk-prod"); + assert.equal(provider["opencode-omniroute-preprod"].options.apiKey, "sk-preprod"); + assert.equal(provider["opencode-omniroute-prod"].options.baseURL, "https://prod.example/v1"); + assert.equal( + provider["opencode-omniroute-preprod"].options.baseURL, + "https://preprod.example/v1" + ); assert.notEqual( - provider["omniroute-prod"], - provider["omniroute-preprod"], + provider["opencode-omniroute-prod"], + provider["opencode-omniroute-preprod"], "blocks are distinct references" ); }); @@ -542,7 +552,7 @@ test("config: multi-instance — two plugins with different providerIds publish test("config + provider share cache: second call uses cached fetch result (single fetch per TTL)", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-shared", baseURL: "https://or.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk-shared", baseURL: "https://or.example/v1" }, }); const fetcher = stubModelsFetcher([MODEL_CLAUDE]); const combosFetcher = stubCombosFetcher([COMBO_CLAUDE_TIER]); @@ -574,7 +584,7 @@ test("config + provider share cache: second call uses cached fetch result (singl test("provider → config order also dedupes (cache populated by provider, consumed by config)", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-reverse", baseURL: "https://or.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk-reverse", baseURL: "https://or.example/v1" }, }); const fetcher = stubModelsFetcher([MODEL_CLAUDE]); const combosFetcher = stubCombosFetcher([]); @@ -654,7 +664,7 @@ test("buildStaticProviderEntry: stripped per-model shape matches sibling @omniro } // Sanity: claude entry has all expected stripped fields. - const claude = block.models["omniroute/claude-sonnet-4-6"]; + const claude = block.models["opencode-omniroute/claude-sonnet-4-6"]; assert.equal(typeof claude.name, "string"); assert.equal(typeof claude.attachment, "boolean"); assert.equal(typeof claude.reasoning, "boolean"); @@ -679,8 +689,8 @@ test("buildStaticProviderEntry: hidden combos are excluded", () => { "https://or.example/v1", "sk-test" ); - assert.equal(block.models["omniroute/claude-tier"], undefined); - assert.ok(block.models["omniroute/claude-sonnet-4-6"]); + assert.equal(block.models["opencode-omniroute/claude-tier"], undefined); + assert.ok(block.models["opencode-omniroute/claude-sonnet-4-6"]); }); // ──────────────────────────────────────────────────────────────────────────── @@ -696,7 +706,7 @@ test("buildStaticProviderEntry: emits modalities.input from raw.input_modalities "https://or.example/v1", "sk-test" ); - const claude = block.models["omniroute/claude-sonnet-4-6"]; + const claude = block.models["opencode-omniroute/claude-sonnet-4-6"]; assert.deepEqual(claude.modalities?.input, ["text", "image"]); assert.deepEqual(claude.modalities?.output, ["text"]); }); @@ -710,7 +720,7 @@ test("buildStaticProviderEntry: never emits limit.input (OC SDK rejects it)", () "https://or.example/v1", "sk-test" ); - const claude = block.models["omniroute/claude-sonnet-4-6"]; + const claude = block.models["opencode-omniroute/claude-sonnet-4-6"]; assert.equal((claude.limit as Record).input, undefined); assert.equal(typeof claude.limit?.context, "number"); assert.equal(typeof claude.limit?.output, "number"); @@ -738,7 +748,7 @@ test("buildStaticProviderEntry: emits cost when enrichment carries pricing", () "sk-test", enrichment ); - const claude = block.models["omniroute/claude-sonnet-4-6"]; + const claude = block.models["opencode-omniroute/claude-sonnet-4-6"]; assert.equal(claude.cost?.input, 3); assert.equal(claude.cost?.output, 15); assert.equal(claude.cost?.cache_read, 0.3); @@ -759,8 +769,8 @@ test("buildStaticProviderEntry: emits release_date when raw carries it; omits wh "https://or.example/v1", "sk-test" ); - assert.equal(block.models["omniroute/claude-with-date"].release_date, "2026-02-19"); - assert.equal(block.models["omniroute/gemini-3-flash"].release_date, undefined); + assert.equal(block.models["opencode-omniroute/claude-with-date"].release_date, "2026-02-19"); + assert.equal(block.models["opencode-omniroute/gemini-3-flash"].release_date, undefined); }); test("buildStaticProviderEntry: combo modalities = intersection of members (LCD)", () => { @@ -789,7 +799,7 @@ test("buildStaticProviderEntry: combo modalities = intersection of members (LCD) "https://or.example/v1", "sk-test" ); - const combo = block.models["omniroute/mixed-tier"]; + const combo = block.models["opencode-omniroute/mixed-tier"]; assert.ok(combo, "combo emitted under slug key"); // claude has text+image, text-only has text → intersection drops image. assert.deepEqual(combo.modalities?.input, ["text"]); @@ -813,7 +823,7 @@ test("OmniRoutePlugin factory exposes config hook alongside auth + provider", as test("config: auth.json entry of wrong type (oauth) → no-op", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "oauth", refresh: "r", access: "a", expires: 0 }, + "opencode-omniroute": { type: "oauth", refresh: "r", access: "a", expires: 0 }, }); const fetcher = stubModelsFetcher([MODEL_CLAUDE]); const combosFetcher = stubCombosFetcher([]); @@ -850,7 +860,7 @@ test("config: readAuthJson throws → treat as missing file (silent fallback)", test("config: initialises input.provider when undefined", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk", baseURL: "https://or.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk", baseURL: "https://or.example/v1" }, }); const fetcher = stubModelsFetcher([MODEL_CLAUDE]); const combosFetcher = stubCombosFetcher([]); @@ -865,7 +875,7 @@ test("config: initialises input.provider when undefined", async () => { await hook(input); const provider = (input as { provider?: Record }).provider; assert.ok(provider, "provider bag initialised"); - assert.ok(provider!.omniroute); + assert.ok(provider!["opencode-omniroute"]); }); // ──────────────────────────────────────────────────────────────────────────── @@ -875,7 +885,7 @@ test("config: initialises input.provider when undefined", async () => { test("config: enrichment fetched + name overlaid on raw-model entries", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, }); const fetcher = stubModelsFetcher([MODEL_CLAUDE, MODEL_GEMINI]); const combosFetcher = stubCombosFetcher([COMBO_CLAUDE_TIER]); @@ -894,19 +904,20 @@ test("config: enrichment fetched + name overlaid on raw-model entries", async () const input = makeInput(); await hook(input); - const entry = (input as { provider: Record }).provider - .omniroute; + const entry = (input as { provider: Record }).provider[ + "opencode-omniroute" + ]; assert.ok(entry); - assert.equal(entry.models["omniroute/claude-sonnet-4-6"].name, "Claude Sonnet 4.6"); - assert.equal(entry.models["omniroute/gemini-3-flash"].name, "Gemini 3 Flash"); + assert.equal(entry.models["opencode-omniroute/claude-sonnet-4-6"].name, "Claude Sonnet 4.6"); + assert.equal(entry.models["opencode-omniroute/gemini-3-flash"].name, "Gemini 3 Flash"); // Combo names still come from /api/combos — enrichment overlay does NOT touch combos. - assert.equal(entry.models["omniroute/claude-tier"].name, "Claude Tier"); + assert.equal(entry.models["opencode-omniroute/claude-tier"].name, "Claude Tier"); assert.equal(enrichmentFetcher.callCount(), 1); }); test("config: features.enrichment=false skips enrichment fetch + keeps raw-id names", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, }); const fetcher = stubModelsFetcher([MODEL_CLAUDE]); const combosFetcher = stubCombosFetcher([]); @@ -924,12 +935,13 @@ test("config: features.enrichment=false skips enrichment fetch + keeps raw-id na const input = makeInput(); await hook(input); - const entry = (input as { provider: Record }).provider - .omniroute; + const entry = (input as { provider: Record }).provider[ + "opencode-omniroute" + ]; assert.ok(entry); assert.equal(enrichmentFetcher.callCount(), 0, "enrichment fetch suppressed by feature flag"); assert.equal( - entry.models["omniroute/claude-sonnet-4-6"].name, + entry.models["opencode-omniroute/claude-sonnet-4-6"].name, "claude-sonnet-4-6", "raw id retained" ); @@ -937,7 +949,7 @@ test("config: features.enrichment=false skips enrichment fetch + keeps raw-id na test("config: enrichment fetcher throws → soft-fail (warn + raw-id static catalog)", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, }); const fetcher = stubModelsFetcher([MODEL_CLAUDE]); const combosFetcher = stubCombosFetcher([]); @@ -951,11 +963,12 @@ test("config: enrichment fetcher throws → soft-fail (warn + raw-id static cata const input = makeInput(); await hook(input); - const entry = (input as { provider: Record }).provider - .omniroute; + const entry = (input as { provider: Record }).provider[ + "opencode-omniroute" + ]; assert.ok(entry, "static block still published on enrichment failure"); assert.equal( - entry.models["omniroute/claude-sonnet-4-6"].name, + entry.models["opencode-omniroute/claude-sonnet-4-6"].name, "claude-sonnet-4-6", "raw id retained" ); @@ -999,7 +1012,7 @@ const MODEL_NV_LLAMA: OmniRouteRawModelEntry = { test("config: usableOnly=false → no filter (existing behavior)", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, }); const fetcher = stubModelsFetcher([MODEL_CC_OPUS, MODEL_NV_LLAMA]); const combosFetcher = stubCombosFetcher([]); @@ -1021,8 +1034,9 @@ test("config: usableOnly=false → no filter (existing behavior)", async () => { const input = makeInput(); await hook(input); - const entry = (input as { provider: Record }).provider - .omniroute; + const entry = (input as { provider: Record }).provider[ + "opencode-omniroute" + ]; assert.ok(entry.models["cc/claude-opus-4-7"], "claude kept"); assert.ok(entry.models["nvidia/llama-3-70b"], "nvidia kept (filter off)"); assert.equal(providersFetcher.callCount(), 0, "providers fetch not called when feature off"); @@ -1030,7 +1044,7 @@ test("config: usableOnly=false → no filter (existing behavior)", async () => { test("config: usableOnly=true → drops models for non-usable providers, keeps usable + unknown", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, }); const fetcher = stubModelsFetcher([ MODEL_CC_OPUS, @@ -1070,8 +1084,9 @@ test("config: usableOnly=true → drops models for non-usable providers, keeps u const input = makeInput(); await hook(input); - const entry = (input as { provider: Record }).provider - .omniroute; + const entry = (input as { provider: Record }).provider[ + "opencode-omniroute" + ]; assert.ok(entry.models["cc/claude-opus-4-7"], "claude kept (active)"); assert.equal(entry.models["nvidia/llama-3-70b"], undefined, "nvidia dropped (error status)"); assert.ok(entry.models["agentrouter/synthetic-1"], "unknown prefix kept (subtract-filter)"); @@ -1080,7 +1095,7 @@ test("config: usableOnly=true → drops models for non-usable providers, keeps u test("config: usableOnly=true + providers fetch fails → soft-fail keeps everything", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, }); const fetcher = stubModelsFetcher([MODEL_CC_OPUS, MODEL_NV_LLAMA]); const combosFetcher = stubCombosFetcher([]); @@ -1101,8 +1116,9 @@ test("config: usableOnly=true + providers fetch fails → soft-fail keeps everyt const input = makeInput(); await hook(input); - const entry = (input as { provider: Record }).provider - .omniroute; + const entry = (input as { provider: Record }).provider[ + "opencode-omniroute" + ]; assert.ok(entry.models["cc/claude-opus-4-7"]); assert.ok(entry.models["nvidia/llama-3-70b"], "soft-fail keeps both"); assert.ok( @@ -1113,7 +1129,7 @@ test("config: usableOnly=true + providers fetch fails → soft-fail keeps everyt test("config: diskCache hydrates stale snapshot when /v1/models throws", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, }); const fetcher = throwingModelsFetcher(); const combosFetcher = stubCombosFetcher([]); @@ -1150,14 +1166,15 @@ test("config: diskCache hydrates stale snapshot when /v1/models throws", async ( const input = makeInput(); await hook(input); - const entry = (input as { provider: Record }).provider - .omniroute; + const entry = (input as { provider: Record }).provider[ + "opencode-omniroute" + ]; assert.ok( - entry.models["omniroute/claude-sonnet-4-6"], + entry.models["opencode-omniroute/claude-sonnet-4-6"], "stale snapshot hydrated into static block" ); assert.equal( - entry.models["omniroute/claude-sonnet-4-6"].name, + entry.models["opencode-omniroute/claude-sonnet-4-6"].name, "Claude Sonnet 4.6 (cached)", "stale enrichment also reused" ); @@ -1170,7 +1187,7 @@ test("config: diskCache hydrates stale snapshot when /v1/models throws", async ( test("config: cached rawEnrichment from earlier provider hook is reused (no refetch)", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-shared", baseURL: "https://or.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk-shared", baseURL: "https://or.example/v1" }, }); const fetcher = stubModelsFetcher([MODEL_CLAUDE]); const combosFetcher = stubCombosFetcher([]); @@ -1202,9 +1219,10 @@ test("config: cached rawEnrichment from earlier provider hook is reused (no refe await configHook(input); assert.equal(enrichmentFetcher.callCount(), 1, "config reused cached enrichment"); - const entry = (input as { provider: Record }).provider - .omniroute; - assert.equal(entry.models["omniroute/claude-sonnet-4-6"].name, "Claude Sonnet 4.6"); + const entry = (input as { provider: Record }).provider[ + "opencode-omniroute" + ]; + assert.equal(entry.models["opencode-omniroute/claude-sonnet-4-6"].name, "Claude Sonnet 4.6"); }); // ───────────────────────────────────────────────────────────────────── @@ -1215,7 +1233,7 @@ test("config: cached rawEnrichment from earlier provider hook is reused (no refe test("config: providerTag (default-on) prepends ' - ' to enriched raw-model names", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, }); const fetcher = stubModelsFetcher([MODEL_CLAUDE, MODEL_GEMINI]); const combosFetcher = stubCombosFetcher([COMBO_CLAUDE_TIER]); @@ -1250,18 +1268,25 @@ test("config: providerTag (default-on) prepends ' - ' to enriched raw- const input = makeInput(); await hook(input); - const entry = (input as { provider: Record }).provider - .omniroute; + const entry = (input as { provider: Record }).provider[ + "opencode-omniroute" + ]; assert.ok(entry); - assert.equal(entry.models["omniroute/claude-sonnet-4-6"].name, "Claude - Claude Sonnet 4.6"); - assert.equal(entry.models["omniroute/gemini-3-flash"].name, "Gemini-cli - Gemini 3 Flash"); + assert.equal( + entry.models["opencode-omniroute/claude-sonnet-4-6"].name, + "Claude - Claude Sonnet 4.6" + ); + assert.equal( + entry.models["opencode-omniroute/gemini-3-flash"].name, + "Gemini-cli - Gemini 3 Flash" + ); // Combos stay untouched — `Combo: ` prefix already conveys multi-upstream. - assert.equal(entry.models["omniroute/claude-tier"].name, "Claude Tier"); + assert.equal(entry.models["opencode-omniroute/claude-tier"].name, "Claude Tier"); }); test("config: providerTag=false suppresses the suffix", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, }); const fetcher = stubModelsFetcher([MODEL_CLAUDE]); const combosFetcher = stubCombosFetcher([]); @@ -1279,10 +1304,11 @@ test("config: providerTag=false suppresses the suffix", async () => { const input = makeInput(); await hook(input); - const entry = (input as { provider: Record }).provider - .omniroute; + const entry = (input as { provider: Record }).provider[ + "opencode-omniroute" + ]; assert.equal( - entry.models["omniroute/claude-sonnet-4-6"].name, + entry.models["opencode-omniroute/claude-sonnet-4-6"].name, "Claude Sonnet 4.6", "enriched name kept, provider tag suppressed" ); @@ -1290,7 +1316,7 @@ test("config: providerTag=false suppresses the suffix", async () => { test("config: providerTag falls back to UPPER(alias) when providerDisplayName missing", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, }); const fetcher = stubModelsFetcher([MODEL_CLAUDE]); const combosFetcher = stubCombosFetcher([]); @@ -1311,14 +1337,15 @@ test("config: providerTag falls back to UPPER(alias) when providerDisplayName mi const input = makeInput(); await hook(input); - const entry = (input as { provider: Record }).provider - .omniroute; - assert.equal(entry.models["omniroute/claude-sonnet-4-6"].name, "CC - Claude Sonnet 4.6"); + const entry = (input as { provider: Record }).provider[ + "opencode-omniroute" + ]; + assert.equal(entry.models["opencode-omniroute/claude-sonnet-4-6"].name, "CC - Claude Sonnet 4.6"); }); test("config: providerTag skipped entirely when neither providerDisplayName nor providerAlias set", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, }); const fetcher = stubModelsFetcher([MODEL_CLAUDE]); const combosFetcher = stubCombosFetcher([]); @@ -1337,14 +1364,15 @@ test("config: providerTag skipped entirely when neither providerDisplayName nor const input = makeInput(); await hook(input); - const entry = (input as { provider: Record }).provider - .omniroute; - assert.equal(entry.models["omniroute/claude-sonnet-4-6"].name, "Claude Sonnet 4.6"); + const entry = (input as { provider: Record }).provider[ + "opencode-omniroute" + ]; + assert.equal(entry.models["opencode-omniroute/claude-sonnet-4-6"].name, "Claude Sonnet 4.6"); }); test("config: providerTag is idempotent — second hook call doesn't double-suffix", async () => { const readAuthJson = stubReadAuthJson({ - omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, + "opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" }, }); const fetcher = stubModelsFetcher([MODEL_CLAUDE]); const combosFetcher = stubCombosFetcher([]); @@ -1363,16 +1391,24 @@ test("config: providerTag is idempotent — second hook call doesn't double-suff const inputA = makeInput(); await hook(inputA); - const entryA = (inputA as { provider: Record }).provider - .omniroute; - assert.equal(entryA.models["omniroute/claude-sonnet-4-6"].name, "Claude - Claude Sonnet 4.6"); + const entryA = (inputA as { provider: Record }).provider[ + "opencode-omniroute" + ]; + assert.equal( + entryA.models["opencode-omniroute/claude-sonnet-4-6"].name, + "Claude - Claude Sonnet 4.6" + ); // Second invocation (cache hit) — name must still be single-suffixed. const inputB = makeInput(); await hook(inputB); - const entryB = (inputB as { provider: Record }).provider - .omniroute; - assert.equal(entryB.models["omniroute/claude-sonnet-4-6"].name, "Claude - Claude Sonnet 4.6"); + const entryB = (inputB as { provider: Record }).provider[ + "opencode-omniroute" + ]; + assert.equal( + entryB.models["opencode-omniroute/claude-sonnet-4-6"].name, + "Claude - Claude Sonnet 4.6" + ); }); // ──────────────────────────────────────────────────────────────────────────── @@ -1424,7 +1460,7 @@ test("buildStaticProviderEntry: nested combo-ref context is the bottleneck acros ); // Pre-fix: Parent would advertise 200_000 (only raw-big counted). // Post-fix: Parent should advertise 8_000 (TinyCombo bottleneck). - const parent = block.models["omniroute/parent"]; + const parent = block.models["opencode-omniroute/parent"]; assert.ok(parent, "Parent combo must be in the static catalog"); assert.equal(parent.limit?.context, 8_000); }); diff --git a/@omniroute/opencode-plugin/tests/features.test.ts b/@omniroute/opencode-plugin/tests/features.test.ts index 20c54b61d5..73ee37a72b 100644 --- a/@omniroute/opencode-plugin/tests/features.test.ts +++ b/@omniroute/opencode-plugin/tests/features.test.ts @@ -376,7 +376,7 @@ test("provider hook: enrichment fetcher called when features.enrichment !== fals ); const out = await hook.models!({} as never, { auth: apiAuth("sk") as never }); assert.equal(called, 1, "enrichment fetcher called once"); - const m = out["omniroute/claude-sonnet-4-6"]; + const m = out["opencode-omniroute/claude-sonnet-4-6"]; assert.equal(m.name, "Claude Sonnet 4.6", "enrichment name overlay applied"); assert.equal(m.cost.input, 3, "enrichment pricing applied"); assert.equal(m.cost.output, 15); @@ -401,7 +401,11 @@ test("provider hook: enrichment fetcher NOT called when features.enrichment:fals ); const out = await hook.models!({} as never, { auth: apiAuth("sk") as never }); assert.equal(called, 0, "enrichment fetcher NOT called when gated off"); - assert.equal(out["omniroute/claude-sonnet-4-6"].name, "claude-sonnet-4-6", "raw id preserved"); + assert.equal( + out["opencode-omniroute/claude-sonnet-4-6"].name, + "claude-sonnet-4-6", + "raw id preserved" + ); }); test("provider hook: compression metadata fetcher NOT called by default (opt-in)", async () => { @@ -459,7 +463,7 @@ test("provider hook: compression metadata fetcher called when opted in", async ( ); const out = await hook.models!({} as never, { auth: apiAuth("sk") as never }); assert.equal(called, 1, "compression metadata fetcher called"); - const combo = out["omniroute/claude-primary"]; + const combo = out["opencode-omniroute/claude-primary"]; assert.ok(combo, "combo entry present"); assert.match( combo.name, @@ -473,7 +477,7 @@ test("provider hook: compression metadata fetcher called when opted in", async ( // ───────────────────────────────────────────────────────────────────────── const stubAuthJson = (apiKey: string) => async () => ({ - omniroute: { type: "api" as const, key: apiKey }, + "opencode-omniroute": { type: "api" as const, key: apiKey }, }); test("config hook: MCP auto-emit OFF by default (no mcp entry)", async () => { @@ -488,7 +492,7 @@ test("config hook: MCP auto-emit OFF by default (no mcp entry)", async () => { ); const input: { provider?: Record; mcp?: Record } = {}; await hook(input as never); - assert.ok(input.provider?.omniroute, "provider block written"); + assert.ok(input.provider?.["opencode-omniroute"], "provider block written"); assert.equal(input.mcp, undefined, "no mcp block written"); }); @@ -508,7 +512,7 @@ test("config hook: features.mcpAutoEmit:true writes mcp entry with provider apiK ); const input: { provider?: Record; mcp?: Record } = {}; await hook(input as never); - const entry = input.mcp?.omniroute as + const entry = input.mcp?.["opencode-omniroute"] as | { type: string; url: string; enabled: boolean; headers: Record } | undefined; assert.ok(entry, "mcp entry written"); @@ -538,7 +542,7 @@ test("config hook: features.mcpToken overrides provider apiKey in mcp Bearer", a ); const input: { provider?: Record; mcp?: Record } = {}; await hook(input as never); - const entry = input.mcp?.omniroute as { headers: Record }; + const entry = input.mcp?.["opencode-omniroute"] as { headers: Record }; assert.equal( entry.headers.Authorization, "Bearer sk-mcp-narrower", @@ -561,11 +565,11 @@ test("config hook: existing operator mcp. wins (no overwrite)", asyn } ); const input: { provider?: Record; mcp?: Record } = { - mcp: { omniroute: { type: "custom-user-entry", url: "https://manual.example/mcp" } }, + mcp: { "opencode-omniroute": { type: "custom-user-entry", url: "https://manual.example/mcp" } }, }; await hook(input as never); assert.deepEqual( - input.mcp?.omniroute, + input.mcp?.["opencode-omniroute"], { type: "custom-user-entry", url: "https://manual.example/mcp" }, "operator override preserved" ); @@ -580,7 +584,7 @@ test("config hook: features.mcpAutoEmit:true with /v1 in baseURL → strips corr }, { readAuthJson: async () => ({ - "omniroute-preprod": { type: "api" as const, key: "sk-preprod" }, + "opencode-omniroute-preprod": { type: "api" as const, key: "sk-preprod" }, }), fetcher: async () => SAMPLE_RAW, combosFetcher: async () => [], @@ -589,7 +593,7 @@ test("config hook: features.mcpAutoEmit:true with /v1 in baseURL → strips corr ); const input: { provider?: Record; mcp?: Record } = {}; await hook(input as never); - const entry = input.mcp?.["omniroute-preprod"] as { url: string }; + const entry = input.mcp?.["opencode-omniroute-preprod"] as { url: string }; assert.equal( entry.url, "https://or-preprod.example.com/api/mcp/stream", diff --git a/@omniroute/opencode-plugin/tests/multi-instance.test.ts b/@omniroute/opencode-plugin/tests/multi-instance.test.ts index a7852a95b9..352d6ad045 100644 --- a/@omniroute/opencode-plugin/tests/multi-instance.test.ts +++ b/@omniroute/opencode-plugin/tests/multi-instance.test.ts @@ -38,8 +38,8 @@ test("multi-instance: two plugin invocations bind to their own providerId", asyn baseURL: "https://b.example/v1", }); - assert.equal(a.auth?.provider, "omniroute-prod"); - assert.equal(b.auth?.provider, "omniroute-preprod"); + assert.equal(a.auth?.provider, "opencode-omniroute-prod"); + assert.equal(b.auth?.provider, "opencode-omniroute-preprod"); }); test("multi-instance: hook objects + nested arrays are independent references", async () => { @@ -70,8 +70,8 @@ test("multi-instance: identical opts twice still yield independent objects", asy assert.notEqual(first.auth, second.auth); assert.notEqual(first.auth?.methods, second.auth?.methods); // Same provider id is fine — what matters is no shared mutable state. - assert.equal(first.auth?.provider, "twin"); - assert.equal(second.auth?.provider, "twin"); + assert.equal(first.auth?.provider, "opencode-twin"); + assert.equal(second.auth?.provider, "opencode-twin"); }); test("multi-instance: mutating instance A's auth.methods does not affect instance B", async () => { @@ -132,5 +132,5 @@ test("multi-instance: invalid opts on one instance does not poison the other", a providerId: "recovered", baseURL: "https://ok.example/v1", }); - assert.equal(ok.auth?.provider, "recovered"); + assert.equal(ok.auth?.provider, "opencode-recovered"); }); diff --git a/@omniroute/opencode-plugin/tests/provider.test.ts b/@omniroute/opencode-plugin/tests/provider.test.ts index 2e4893216d..e4849205ac 100644 --- a/@omniroute/opencode-plugin/tests/provider.test.ts +++ b/@omniroute/opencode-plugin/tests/provider.test.ts @@ -75,7 +75,7 @@ const apiAuth = (key: string, baseURL?: string): unknown => test("createOmniRouteProviderHook: default providerId is 'omniroute'", () => { const hook = createOmniRouteProviderHook(undefined, { combosFetcher: async () => [] }); - assert.equal(hook.id, "omniroute"); + assert.equal(hook.id, "opencode-omniroute"); }); test("createOmniRouteProviderHook: custom providerId binds to hook.id (multi-instance)", () => { @@ -87,8 +87,8 @@ test("createOmniRouteProviderHook: custom providerId binds to hook.id (multi-ins { providerId: "omniroute-local" }, { combosFetcher: async () => [] } ); - assert.equal(a.id, "omniroute-preprod"); - assert.equal(b.id, "omniroute-local"); + assert.equal(a.id, "opencode-omniroute-preprod"); + assert.equal(b.id, "opencode-omniroute-local"); }); test("models: extracts apiKey from ctx.auth (type=api) and calls fetcher with it", async () => { @@ -101,7 +101,7 @@ test("models: extracts apiKey from ctx.auth (type=api) and calls fetcher with it assert.equal(fetcher.callCount(), 1); assert.deepEqual(fetcher.callsBy()[0], ["https://or.example.com/v1", "sk-abc"]); assert.equal(Object.keys(out).length, 3); - assert.ok(out["omniroute/claude-primary"]); + assert.ok(out["opencode-omniroute/claude-primary"]); }); test("models: returns {} when ctx.auth is null/undefined/wrong-type/empty-key", async () => { @@ -152,13 +152,13 @@ test("models: maps a sample /v1/models entry to ModelV2 (sanity)", async () => { { fetcher, combosFetcher: async () => [] } ); const out = await hook.models!({} as never, { auth: apiAuth("sk-abc") as never }); - const claude = out["omniroute/claude-primary"]; + const claude = out["opencode-omniroute/claude-primary"]; assert.ok(claude, "claude-primary present"); // `mapRawModelToModelV2` stamps the provider prefix on the id so OC's // static-catalog reader resolves `(providerID, modelID)` from the key. - assert.equal(claude.id, "omniroute/claude-primary"); + assert.equal(claude.id, "opencode-omniroute/claude-primary"); assert.equal(claude.name, "claude-primary"); - assert.equal(claude.providerID, "omniroute"); + assert.equal(claude.providerID, "opencode-omniroute"); assert.equal(claude.api.id, "openai-compatible"); assert.equal(claude.api.url, "https://or.example.com/v1"); assert.equal(claude.api.npm, "@ai-sdk/openai-compatible"); diff --git a/@omniroute/opencode-plugin/tests/scaffold.test.ts b/@omniroute/opencode-plugin/tests/scaffold.test.ts index 42e1f09404..e860171f75 100644 --- a/@omniroute/opencode-plugin/tests/scaffold.test.ts +++ b/@omniroute/opencode-plugin/tests/scaffold.test.ts @@ -26,7 +26,7 @@ test("scaffold: default export is v1 plugin shape { id, server: OmniRoutePlugin test("resolveOmniRoutePluginOptions: defaults", () => { const r = resolveOmniRoutePluginOptions(); - assert.equal(r.providerId, "omniroute"); + assert.equal(r.providerId, "opencode-omniroute"); assert.equal(r.displayName, "OmniRoute"); assert.equal(r.modelCacheTtl, 300_000); assert.equal(r.baseURL, undefined); @@ -34,8 +34,8 @@ test("resolveOmniRoutePluginOptions: defaults", () => { test("resolveOmniRoutePluginOptions: custom providerId derives displayName", () => { const r = resolveOmniRoutePluginOptions({ providerId: "omniroute-preprod" }); - assert.equal(r.providerId, "omniroute-preprod"); - assert.equal(r.displayName, "OmniRoute (omniroute-preprod)"); + assert.equal(r.providerId, "opencode-omniroute-preprod"); + assert.equal(r.displayName, "OmniRoute (opencode-omniroute-preprod)"); }); test("resolveOmniRoutePluginOptions: explicit displayName wins", () => {