diff --git a/packages/opencode/src/cli/cmd/auth.ts b/packages/opencode/src/cli/cmd/auth.ts
index 34e2269d0c1..23bb816538f 100644
--- a/packages/opencode/src/cli/cmd/auth.ts
+++ b/packages/opencode/src/cli/cmd/auth.ts
@@ -265,6 +265,15 @@ export const AuthLoginCommand = cmd({
filtered[key] = value
}
}
+ // Add Maple AI (not in models.dev, models discovered dynamically from proxy)
+ if ((enabled ? enabled.has("maple") : true) && !disabled.has("maple")) {
+ filtered["maple"] = {
+ id: "maple",
+ name: "Maple AI",
+ env: ["MAPLE_API_KEY"],
+ models: {},
+ }
+ }
return filtered
})
@@ -358,6 +367,19 @@ export const AuthLoginCommand = cmd({
)
}
+ if (provider === "maple") {
+ prompts.log.info(
+ "Maple AI is a TEE-based private AI provider.\n\n" +
+ "Setup:\n" +
+ " 1. Start the Maple Proxy (desktop app or Docker)\n" +
+ " 2. Generate an API key in the Maple app\n" +
+ " 3. Enter your API key below\n\n" +
+ "The default proxy URL is http://127.0.0.1:8080/v1\n" +
+ "To use a different URL, add to opencode.json:\n" +
+ ' { "provider": { "maple": { "options": { "baseURL": "http://your-url/v1" } } } }',
+ )
+ }
+
const key = await prompts.password({
message: "Enter your API key",
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx
index f8be5577b35..b224595c847 100644
--- a/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx
@@ -246,6 +246,19 @@ function ApiMethod(props: ApiMethodProps) {
Go to https://opencode.ai/zen to get a key
+ ) : props.providerID === "maple" ? (
+
+
+ Maple AI is a TEE-based private AI provider. Start the Maple Proxy (desktop app or Docker) and generate an
+ API key in the Maple app.
+
+
+ Default proxy URL: http://127.0.0.1:8080/v1
+
+
+ To use a different URL, add to opencode.json: provider.maple.options.baseURL
+
+
) : undefined
}
onConfirm={async (value) => {
diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts
index 1cad3b3162a..92045a1dade 100644
--- a/packages/opencode/src/provider/provider.ts
+++ b/packages/opencode/src/provider/provider.ts
@@ -528,6 +528,69 @@ export namespace Provider {
},
}
},
+ maple: async (input) => {
+ const config = await Config.get()
+ const baseURL = config.provider?.["maple"]?.options?.baseURL ?? "http://127.0.0.1:8080/v1"
+
+ const auth = await Auth.get("maple")
+ const apiKey = auth?.type === "api" ? auth.key : Env.get("MAPLE_API_KEY")
+
+ if (!apiKey) return { autoload: false }
+
+ // Dynamically fetch models from the Maple proxy
+ try {
+ const response = await fetch(`${baseURL}/models`, {
+ headers: { Authorization: `Bearer ${apiKey}` },
+ signal: AbortSignal.timeout(5000),
+ })
+ if (!response.ok) {
+ log.warn("Failed to fetch Maple models", { status: response.status })
+ return { autoload: false }
+ }
+ const data = (await response.json()) as { data?: Array<{ id: string }> }
+ const models = data.data ?? []
+
+ for (const model of models) {
+ input.models[model.id] = {
+ id: model.id,
+ providerID: "maple",
+ name: model.id,
+ api: {
+ id: model.id,
+ url: baseURL,
+ npm: "@ai-sdk/openai-compatible",
+ },
+ status: "active",
+ headers: {},
+ options: {},
+ cost: { input: 0, output: 0, cache: { read: 0, write: 0 } },
+ limit: { context: 128000, output: 8192 },
+ capabilities: {
+ temperature: true,
+ reasoning: false,
+ attachment: false,
+ toolcall: true,
+ input: { text: true, audio: false, image: false, video: false, pdf: false },
+ output: { text: true, audio: false, image: false, video: false, pdf: false },
+ interleaved: false,
+ },
+ release_date: "",
+ variants: {},
+ }
+ }
+ } catch (e) {
+ log.warn("Failed to connect to Maple proxy", { error: e })
+ return { autoload: false }
+ }
+
+ return {
+ autoload: Object.keys(input.models).length > 0,
+ options: {
+ baseURL,
+ apiKey,
+ },
+ }
+ },
}
export const Model = z
@@ -734,6 +797,16 @@ export namespace Provider {
}
}
+ // Add Maple AI provider (models are populated dynamically from the proxy)
+ database["maple"] = {
+ id: "maple",
+ name: "Maple AI",
+ source: "custom",
+ env: ["MAPLE_API_KEY"],
+ options: {},
+ models: {},
+ }
+
function mergeProvider(providerID: string, provider: Partial) {
const existing = providers[providerID]
if (existing) {
diff --git a/packages/opencode/src/server/routes/provider.ts b/packages/opencode/src/server/routes/provider.ts
index 872b48be79d..bafe35a69fb 100644
--- a/packages/opencode/src/server/routes/provider.ts
+++ b/packages/opencode/src/server/routes/provider.ts
@@ -46,6 +46,15 @@ export const ProviderRoutes = lazy(() =>
filteredProviders[key] = value
}
}
+ // Add Maple AI (not in models.dev, models discovered dynamically from proxy)
+ if ((enabled ? enabled.has("maple") : true) && !disabled.has("maple")) {
+ filteredProviders["maple"] = {
+ id: "maple",
+ name: "Maple AI",
+ env: ["MAPLE_API_KEY"],
+ models: {},
+ }
+ }
const connected = await Provider.list()
const providers = Object.assign(
@@ -54,7 +63,11 @@ export const ProviderRoutes = lazy(() =>
)
return c.json({
all: Object.values(providers),
- default: mapValues(providers, (item) => Provider.sort(Object.values(item.models))[0].id),
+ default: mapValues(providers, (item) => {
+ const models = Object.values(item.models)
+ if (models.length === 0) return ""
+ return Provider.sort(models)[0].id
+ }),
connected: Object.keys(connected),
})
},