import { emptyPluginConfigSchema } from "clawdbot/plugin-sdk"; import { loginQwenPortalOAuth } from "./oauth.js"; const PROVIDER_ID = "qwen-portal"; const PROVIDER_LABEL = "Qwen"; const DEFAULT_MODEL = "qwen-portal/coder-model"; const DEFAULT_BASE_URL = "https://portal.qwen.ai/v1"; const DEFAULT_CONTEXT_WINDOW = 128000; const DEFAULT_MAX_TOKENS = 8192; const OAUTH_PLACEHOLDER = "qwen-oauth"; function normalizeBaseUrl(value: string | undefined): string { const raw = value?.trim() || DEFAULT_BASE_URL; const withProtocol = raw.startsWith("http") ? raw : `https://${raw}`; return withProtocol.endsWith("/v1") ? withProtocol : `${withProtocol.replace(/\/+$/, "")}/v1`; } function buildModelDefinition(params: { id: string; name: string; input: Array<"text" | "image"> }) { return { id: params.id, name: params.name, reasoning: false, input: params.input, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: DEFAULT_CONTEXT_WINDOW, maxTokens: DEFAULT_MAX_TOKENS, }; } const qwenPortalPlugin = { id: "qwen-portal-auth", name: "Qwen OAuth", description: "OAuth flow for Qwen (free-tier) models", configSchema: emptyPluginConfigSchema(), register(api) { api.registerProvider({ id: PROVIDER_ID, label: PROVIDER_LABEL, docsPath: "/providers/qwen", aliases: ["qwen"], auth: [ { id: "device", label: "Qwen OAuth", hint: "Device code login", kind: "device_code", run: async (ctx) => { const progress = ctx.prompter.progress("Starting Qwen OAuth…"); try { const result = await loginQwenPortalOAuth({ openUrl: ctx.openUrl, note: ctx.prompter.note, progress, }); progress.stop("Qwen OAuth complete"); const profileId = `${PROVIDER_ID}:default`; const baseUrl = normalizeBaseUrl(result.resourceUrl); return { profiles: [ { profileId, credential: { type: "oauth", provider: PROVIDER_ID, access: result.access, refresh: result.refresh, expires: result.expires, }, }, ], configPatch: { models: { providers: { [PROVIDER_ID]: { baseUrl, apiKey: OAUTH_PLACEHOLDER, api: "openai-completions", models: [ buildModelDefinition({ id: "coder-model", name: "Qwen Coder", input: ["text"], }), buildModelDefinition({ id: "vision-model", name: "Qwen Vision", input: ["text", "image"], }), ], }, }, }, agents: { defaults: { models: { "qwen-portal/coder-model": { alias: "qwen" }, "qwen-portal/vision-model": {}, }, }, }, }, defaultModel: DEFAULT_MODEL, notes: [ "Qwen OAuth tokens auto-refresh. Re-run login if refresh fails or access is revoked.", `Base URL defaults to ${DEFAULT_BASE_URL}. Override models.providers.${PROVIDER_ID}.baseUrl if needed.`, ], }; } catch (err) { progress.stop("Qwen OAuth failed"); await ctx.prompter.note( "If OAuth fails, verify your Qwen account has portal access and try again.", "Qwen OAuth", ); throw err; } }, }, ], }); }, }; export default qwenPortalPlugin;