diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 90c4266051..4650918eab 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -1247,6 +1247,9 @@ export namespace Provider { return { models: languages, providers, + // altimate_change start — expose full provider database (including unauthenticated custom providers) + database, + // altimate_change end sdk, modelLoaders, varsLoaders, @@ -1257,6 +1260,12 @@ export namespace Provider { return state().then((state) => state.providers) } + // altimate_change start — expose full provider database (including unauthenticated custom providers) + export async function all() { + return state().then((state) => state.database) + } + // altimate_change end + async function getSDK(model: Model) { try { using _ = log.time("getSDK", { diff --git a/packages/opencode/src/server/routes/provider.ts b/packages/opencode/src/server/routes/provider.ts index fc716d25cb..7fb5344517 100644 --- a/packages/opencode/src/server/routes/provider.ts +++ b/packages/opencode/src/server/routes/provider.ts @@ -49,8 +49,19 @@ export const ProviderRoutes = lazy(() => } const connected = await Provider.list() + // altimate_change start — include custom providers (e.g. Snowflake Cortex) even when not yet authenticated + const allDatabase = await Provider.all() + const customProviders: Record = {} + for (const [key, value] of Object.entries(allDatabase)) { + if (key in filteredProviders || key in connected) continue + if ((enabled ? enabled.has(key) : true) && !disabled.has(key)) { + customProviders[key] = value + } + } + // altimate_change end const providers = Object.assign( mapValues(filteredProviders, (x) => Provider.fromModelsDevProvider(x)), + customProviders, connected, ) return c.json({ diff --git a/packages/opencode/test/provider/snowflake.test.ts b/packages/opencode/test/provider/snowflake.test.ts index 3f16230fc2..f4e20db74e 100644 --- a/packages/opencode/test/provider/snowflake.test.ts +++ b/packages/opencode/test/provider/snowflake.test.ts @@ -606,3 +606,111 @@ describe("snowflake-cortex provider", () => { } }) }) + +// --------------------------------------------------------------------------- +// Provider.all() — unauthenticated discoverability +// --------------------------------------------------------------------------- + +describe("Provider.all() discoverability", () => { + test("includes snowflake-cortex even without oauth auth", async () => { + const savedAuth = await Auth.get("snowflake-cortex") + if (savedAuth) await Auth.remove("snowflake-cortex") + try { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write(path.join(dir, "opencode.json"), JSON.stringify({ $schema: "https://altimate.ai/config.json" })) + }, + }) + await Instance.provide({ + directory: tmp.path, + init: async () => { + Env.remove("SNOWFLAKE_ACCOUNT") + }, + fn: async () => { + const allProviders = await Provider.all() + expect(allProviders["snowflake-cortex"]).toBeDefined() + expect(allProviders["snowflake-cortex"].name).toBe("Snowflake Cortex") + // list() still returns nothing (not authenticated) + const connected = await Provider.list() + expect(connected["snowflake-cortex"]).toBeUndefined() + }, + }) + } finally { + if (savedAuth) await Auth.set("snowflake-cortex", savedAuth) + } + }) + + test("all() includes snowflake-cortex models", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write(path.join(dir, "opencode.json"), JSON.stringify({ $schema: "https://altimate.ai/config.json" })) + }, + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const allProviders = await Provider.all() + const models = allProviders["snowflake-cortex"]?.models + expect(models).toBeDefined() + expect(models["claude-sonnet-4-6"]).toBeDefined() + expect(models["deepseek-r1"]).toBeDefined() + }, + }) + }) + + test("disabled_providers config suppresses snowflake-cortex from all()", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ $schema: "https://altimate.ai/config.json", disabled_providers: ["snowflake-cortex"] }), + ) + }, + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + // Provider.all() returns raw database, config filtering happens at the route level. + // Verify the route-level filtering logic: a disabled provider should not appear + // in the merged provider list used by GET /provider. + const allProviders = await Provider.all() + const connected = await Provider.list() + // Simulate the route filtering (same logic as routes/provider.ts) + const disabled = new Set(["snowflake-cortex"]) + const customProviders: Record = {} + for (const [key, value] of Object.entries(allProviders)) { + if (key in connected) continue + if (!disabled.has(key)) customProviders[key] = value + } + expect(customProviders["snowflake-cortex"]).toBeUndefined() + }, + }) + }) + + test("enabled_providers config suppresses snowflake-cortex when not listed", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ $schema: "https://altimate.ai/config.json", enabled_providers: ["anthropic"] }), + ) + }, + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const allProviders = await Provider.all() + const connected = await Provider.list() + // Simulate route filtering with enabled_providers + // (snowflake-cortex is not in the enabled list, so it should be excluded) + const enabled = new Set(["anthropic"]) + const customProviders: Record = {} + for (const [key, value] of Object.entries(allProviders)) { + if (key in connected) continue + if (enabled.has(key)) customProviders[key] = value + } + expect(customProviders["snowflake-cortex"]).toBeUndefined() + }, + }) + }) +})