diff --git a/app/api/qwen/chat/route.ts b/app/api/qwen/chat/route.ts new file mode 100644 index 0000000..c9adb96 --- /dev/null +++ b/app/api/qwen/chat/route.ts @@ -0,0 +1,70 @@ +import { NextRequest, NextResponse } from "next/server"; +import { createQwenHeaders } from "../constants"; + +const DEFAULT_QWEN_ENDPOINT = "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"; + +function normalizeEndpoint(raw?: string | null): string { + const trimmed = (raw || "").trim(); + if (!trimmed) { + return DEFAULT_QWEN_ENDPOINT; + } + + if (trimmed.endsWith("/chat/completions")) { + return trimmed; + } + + const cleaned = trimmed.replace(/\/$/, ""); + return `${cleaned}/chat/completions`; +} + +export async function POST(request: NextRequest) { + try { + const body = await request.json().catch(() => ({})); + const { endpoint, model, messages, stream } = body || {}; + const authorization = request.headers.get("authorization") || body?.authorization; + + if (!authorization) { + return NextResponse.json( + { error: "Authorization header required" }, + { status: 401 } + ); + } + + const url = normalizeEndpoint(endpoint); + const response = await fetch(url, { + method: "POST", + headers: { + ...createQwenHeaders("application/json"), + Authorization: authorization, + }, + body: JSON.stringify({ + model, + messages, + stream, + }), + }); + + const payload = await response.text(); + if (!response.ok) { + return NextResponse.json( + { error: payload || response.statusText || "Qwen chat failed" }, + { status: response.status } + ); + } + + try { + const data = JSON.parse(payload); + return NextResponse.json(data, { status: response.status }); + } catch { + return NextResponse.json( + { error: payload || "Unexpected response format" }, + { status: 502 } + ); + } + } catch (error) { + return NextResponse.json( + { error: "internal_server_error", message: error instanceof Error ? error.message : "Qwen chat failed" }, + { status: 500 } + ); + } +} diff --git a/app/api/qwen/constants.ts b/app/api/qwen/constants.ts index 403294a..3c89d22 100644 --- a/app/api/qwen/constants.ts +++ b/app/api/qwen/constants.ts @@ -4,3 +4,19 @@ export const QWEN_OAUTH_TOKEN_ENDPOINT = `${QWEN_OAUTH_BASE_URL}/api/v1/oauth2/t export const QWEN_OAUTH_CLIENT_ID = "f0304373b74a44d2b584a3fb70ca9e56"; export const QWEN_OAUTH_SCOPE = "openid profile email model.completion"; export const QWEN_OAUTH_DEVICE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code"; + +export const QWEN_USER_AGENT = + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"; + +export function createQwenHeaders(contentType?: string) { + const headers: Record = { + Accept: "application/json", + "User-Agent": QWEN_USER_AGENT, + }; + + if (contentType) { + headers["Content-Type"] = contentType; + } + + return headers; +} diff --git a/app/api/qwen/oauth/device/route.ts b/app/api/qwen/oauth/device/route.ts index b304051..0d3877e 100644 --- a/app/api/qwen/oauth/device/route.ts +++ b/app/api/qwen/oauth/device/route.ts @@ -3,6 +3,7 @@ import { QWEN_OAUTH_CLIENT_ID, QWEN_OAUTH_DEVICE_CODE_ENDPOINT, QWEN_OAUTH_SCOPE, + createQwenHeaders, } from "../../constants"; export async function POST(request: NextRequest) { @@ -17,12 +18,11 @@ export async function POST(request: NextRequest) { ); } + console.log(`[Qwen Device Auth] Calling ${QWEN_OAUTH_DEVICE_CODE_ENDPOINT}...`); + const response = await fetch(QWEN_OAUTH_DEVICE_CODE_ENDPOINT, { method: "POST", - headers: { - "Content-Type": "application/x-www-form-urlencoded", - Accept: "application/json", - }, + headers: createQwenHeaders("application/x-www-form-urlencoded"), body: new URLSearchParams({ client_id: QWEN_OAUTH_CLIENT_ID, scope: QWEN_OAUTH_SCOPE, @@ -32,18 +32,25 @@ export async function POST(request: NextRequest) { }); const payload = await response.text(); - if (!response.ok) { - return NextResponse.json( - { error: "Device authorization failed", details: payload }, - { status: response.status } - ); + console.log(`[Qwen Device Auth] Response status: ${response.status}`); + + let data; + try { + data = JSON.parse(payload); + } catch { + console.error(`[Qwen Device Auth] Failed to parse response: ${payload}`); + data = { error: payload || "Unknown error from Qwen" }; } - return NextResponse.json(JSON.parse(payload)); + if (!response.ok) { + console.warn(`[Qwen Device Auth] Error response:`, data); + } + + return NextResponse.json(data, { status: response.status }); } catch (error) { console.error("Qwen device authorization failed", error); return NextResponse.json( - { error: "Device authorization failed" }, + { error: "internal_server_error", message: error instanceof Error ? error.message : "Device authorization failed" }, { status: 500 } ); } diff --git a/app/api/qwen/oauth/refresh/route.ts b/app/api/qwen/oauth/refresh/route.ts index 3bd1d38..b894b8a 100644 --- a/app/api/qwen/oauth/refresh/route.ts +++ b/app/api/qwen/oauth/refresh/route.ts @@ -1,5 +1,9 @@ import { NextRequest, NextResponse } from "next/server"; -import { QWEN_OAUTH_CLIENT_ID, QWEN_OAUTH_TOKEN_ENDPOINT } from "../../constants"; +import { + QWEN_OAUTH_CLIENT_ID, + QWEN_OAUTH_TOKEN_ENDPOINT, + createQwenHeaders, +} from "../../constants"; export async function POST(request: NextRequest) { try { @@ -15,10 +19,7 @@ export async function POST(request: NextRequest) { const response = await fetch(QWEN_OAUTH_TOKEN_ENDPOINT, { method: "POST", - headers: { - "Content-Type": "application/x-www-form-urlencoded", - Accept: "application/json", - }, + headers: createQwenHeaders("application/x-www-form-urlencoded"), body: new URLSearchParams({ grant_type: "refresh_token", refresh_token, @@ -27,18 +28,18 @@ export async function POST(request: NextRequest) { }); const payload = await response.text(); - if (!response.ok) { - return NextResponse.json( - { error: "Token refresh failed", details: payload }, - { status: response.status } - ); + let data; + try { + data = JSON.parse(payload); + } catch { + data = { error: payload || "Unknown error from Qwen" }; } - return NextResponse.json(JSON.parse(payload)); + return NextResponse.json(data, { status: response.status }); } catch (error) { console.error("Qwen token refresh failed", error); return NextResponse.json( - { error: "Token refresh failed" }, + { error: "internal_server_error", message: error instanceof Error ? error.message : "Token refresh failed" }, { status: 500 } ); } diff --git a/app/api/qwen/oauth/token/route.ts b/app/api/qwen/oauth/token/route.ts index 13646c1..d14bdc5 100644 --- a/app/api/qwen/oauth/token/route.ts +++ b/app/api/qwen/oauth/token/route.ts @@ -3,6 +3,7 @@ import { QWEN_OAUTH_CLIENT_ID, QWEN_OAUTH_DEVICE_GRANT_TYPE, QWEN_OAUTH_TOKEN_ENDPOINT, + createQwenHeaders, } from "../../constants"; export async function POST(request: NextRequest) { @@ -17,12 +18,11 @@ export async function POST(request: NextRequest) { ); } + console.log(`[Qwen Token Poll] Calling ${QWEN_OAUTH_TOKEN_ENDPOINT} for device_code: ${device_code.slice(0, 8)}...`); + const response = await fetch(QWEN_OAUTH_TOKEN_ENDPOINT, { method: "POST", - headers: { - "Content-Type": "application/x-www-form-urlencoded", - Accept: "application/json", - }, + headers: createQwenHeaders("application/x-www-form-urlencoded"), body: new URLSearchParams({ grant_type: QWEN_OAUTH_DEVICE_GRANT_TYPE, client_id: QWEN_OAUTH_CLIENT_ID, @@ -32,18 +32,25 @@ export async function POST(request: NextRequest) { }); const payload = await response.text(); - if (!response.ok) { - return NextResponse.json( - { error: "Token poll failed", details: payload }, - { status: response.status } - ); + console.log(`[Qwen Token Poll] Response status: ${response.status}`); + + let data; + try { + data = JSON.parse(payload); + } catch { + console.error(`[Qwen Token Poll] Failed to parse response: ${payload}`); + data = { error: payload || "Unknown error from Qwen" }; } - return NextResponse.json(JSON.parse(payload)); + if (data.error && data.error !== "authorization_pending") { + console.warn(`[Qwen Token Poll] Error in response:`, data); + } + + return NextResponse.json(data, { status: response.status }); } catch (error) { console.error("Qwen token poll failed", error); return NextResponse.json( - { error: "Token poll failed" }, + { error: "internal_server_error", message: error instanceof Error ? error.message : "Token poll failed" }, { status: 500 } ); } diff --git a/app/api/qwen/user/route.ts b/app/api/qwen/user/route.ts index ccb5870..71a6844 100644 --- a/app/api/qwen/user/route.ts +++ b/app/api/qwen/user/route.ts @@ -1,5 +1,5 @@ import { NextRequest, NextResponse } from "next/server"; -import { QWEN_OAUTH_BASE_URL } from "../constants"; +import { QWEN_OAUTH_BASE_URL, createQwenHeaders } from "../constants"; export async function GET(request: NextRequest) { try { @@ -14,16 +14,20 @@ export async function GET(request: NextRequest) { const userResponse = await fetch(`${QWEN_OAUTH_BASE_URL}/api/v1/user`, { headers: { + ...createQwenHeaders(), Authorization: `Bearer ${token}`, }, }); if (!userResponse.ok) { const errorText = await userResponse.text(); - return NextResponse.json( - { error: "Failed to fetch user info", details: errorText }, - { status: userResponse.status } - ); + let errorData; + try { + errorData = JSON.parse(errorText); + } catch { + errorData = { error: errorText || "Failed to fetch user info" }; + } + return NextResponse.json(errorData, { status: userResponse.status }); } const userData = await userResponse.json(); @@ -31,7 +35,7 @@ export async function GET(request: NextRequest) { } catch (error) { console.error("Qwen user info failed", error); return NextResponse.json( - { error: "Failed to fetch user info" }, + { error: "internal_server_error", message: error instanceof Error ? error.message : "Failed to fetch user info" }, { status: 500 } ); } diff --git a/app/page.tsx b/app/page.tsx index 946c81b..8f483e8 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { useState, useEffect } from "react"; import Sidebar from "@/components/Sidebar"; import type { View } from "@/components/Sidebar"; import PromptEnhancer from "@/components/PromptEnhancer"; @@ -9,10 +9,16 @@ import ActionPlanGenerator from "@/components/ActionPlanGenerator"; import UXDesignerPrompt from "@/components/UXDesignerPrompt"; import HistoryPanel from "@/components/HistoryPanel"; import SettingsPanel from "@/components/SettingsPanel"; +import modelAdapter from "@/lib/services/adapter-instance"; export default function Home() { const [currentView, setCurrentView] = useState("enhance"); + useEffect(() => { + console.log("[Home] Initializing Qwen OAuth service on client..."); + modelAdapter["qwenService"]["initialize"]?.(); + }, []); + const renderContent = () => { switch (currentView) { case "enhance": diff --git a/components/ActionPlanGenerator.tsx b/components/ActionPlanGenerator.tsx index 9cfdf74..dd4b933 100644 --- a/components/ActionPlanGenerator.tsx +++ b/components/ActionPlanGenerator.tsx @@ -71,7 +71,9 @@ export default function ActionPlanGenerator() { } const apiKey = apiKeys[selectedProvider]; - if (!apiKey || !apiKey.trim()) { + const isQwenOAuth = selectedProvider === "qwen" && modelAdapter.hasQwenAuth(); + + if (!isQwenOAuth && (!apiKey || !apiKey.trim())) { setError(`Please configure your ${selectedProvider.toUpperCase()} API key in Settings`); return; } @@ -79,9 +81,13 @@ export default function ActionPlanGenerator() { setProcessing(true); setError(null); + console.log("[ActionPlanGenerator] Starting action plan generation...", { selectedProvider, selectedModel, hasQwenAuth: modelAdapter.hasQwenAuth() }); + try { const result = await modelAdapter.generateActionPlan(currentPrompt, selectedProvider, selectedModel); + console.log("[ActionPlanGenerator] Generation result:", result); + if (result.success && result.data) { const newPlan = { id: Math.random().toString(36).substr(2, 9), @@ -100,9 +106,11 @@ export default function ActionPlanGenerator() { }; setActionPlan(newPlan); } else { + console.error("[ActionPlanGenerator] Generation failed:", result.error); setError(result.error || "Failed to generate action plan"); } } catch (err) { + console.error("[ActionPlanGenerator] Generation error:", err); setError(err instanceof Error ? err.message : "An error occurred"); } finally { setProcessing(false); diff --git a/components/PRDGenerator.tsx b/components/PRDGenerator.tsx index ea915da..6aa070e 100644 --- a/components/PRDGenerator.tsx +++ b/components/PRDGenerator.tsx @@ -78,7 +78,9 @@ export default function PRDGenerator() { } const apiKey = apiKeys[selectedProvider]; - if (!apiKey || !apiKey.trim()) { + const isQwenOAuth = selectedProvider === "qwen" && modelAdapter.hasQwenAuth(); + + if (!isQwenOAuth && (!apiKey || !apiKey.trim())) { setError(`Please configure your ${selectedProvider.toUpperCase()} API key in Settings`); return; } @@ -86,9 +88,13 @@ export default function PRDGenerator() { setProcessing(true); setError(null); + console.log("[PRDGenerator] Starting PRD generation...", { selectedProvider, selectedModel, hasQwenAuth: modelAdapter.hasQwenAuth() }); + try { const result = await modelAdapter.generatePRD(currentPrompt, selectedProvider, selectedModel); + console.log("[PRDGenerator] Generation result:", result); + if (result.success && result.data) { const newPRD = { id: Math.random().toString(36).substr(2, 9), @@ -105,9 +111,11 @@ export default function PRDGenerator() { }; setPRD(newPRD); } else { + console.error("[PRDGenerator] Generation failed:", result.error); setError(result.error || "Failed to generate PRD"); } } catch (err) { + console.error("[PRDGenerator] Generation error:", err); setError(err instanceof Error ? err.message : "An error occurred"); } finally { setProcessing(false); diff --git a/components/PromptEnhancer.tsx b/components/PromptEnhancer.tsx index 15dc7ca..81fa6c1 100644 --- a/components/PromptEnhancer.tsx +++ b/components/PromptEnhancer.tsx @@ -71,7 +71,9 @@ export default function PromptEnhancer() { } const apiKey = apiKeys[selectedProvider]; - if (!apiKey || !apiKey.trim()) { + const isQwenOAuth = selectedProvider === "qwen" && modelAdapter.hasQwenAuth(); + + if (!isQwenOAuth && (!apiKey || !apiKey.trim())) { setError(`Please configure your ${selectedProvider.toUpperCase()} API key in Settings`); return; } @@ -79,15 +81,21 @@ export default function PromptEnhancer() { setProcessing(true); setError(null); + console.log("[PromptEnhancer] Starting enhancement...", { selectedProvider, selectedModel, hasQwenAuth: modelAdapter.hasQwenAuth() }); + try { const result = await modelAdapter.enhancePrompt(currentPrompt, selectedProvider, selectedModel); + console.log("[PromptEnhancer] Enhancement result:", result); + if (result.success && result.data) { setEnhancedPrompt(result.data); } else { + console.error("[PromptEnhancer] Enhancement failed:", result.error); setError(result.error || "Failed to enhance prompt"); } } catch (err) { + console.error("[PromptEnhancer] Enhancement error:", err); setError(err instanceof Error ? err.message : "An error occurred"); } finally { setProcessing(false); diff --git a/components/SettingsPanel.tsx b/components/SettingsPanel.tsx index 15e4c05..57aeb71 100644 --- a/components/SettingsPanel.tsx +++ b/components/SettingsPanel.tsx @@ -77,6 +77,7 @@ export default function SettingsPanel() { try { const token = await modelAdapter.startQwenOAuth(); setQwenTokens(token); + modelAdapter.updateQwenTokens(token); } catch (error) { console.error("Qwen OAuth failed", error); window.alert( diff --git a/components/UXDesignerPrompt.tsx b/components/UXDesignerPrompt.tsx index f97b3d1..891c213 100644 --- a/components/UXDesignerPrompt.tsx +++ b/components/UXDesignerPrompt.tsx @@ -70,7 +70,9 @@ export default function UXDesignerPrompt() { } const apiKey = apiKeys[selectedProvider]; - if (!apiKey || !apiKey.trim()) { + const isQwenOAuth = selectedProvider === "qwen" && modelAdapter.hasQwenAuth(); + + if (!isQwenOAuth && (!apiKey || !apiKey.trim())) { setError(`Please configure your ${selectedProvider.toUpperCase()} API key in Settings`); return; } @@ -79,16 +81,22 @@ export default function UXDesignerPrompt() { setError(null); setGeneratedPrompt(null); + console.log("[UXDesignerPrompt] Starting generation...", { selectedProvider, selectedModel, hasQwenAuth: modelAdapter.hasQwenAuth() }); + try { const result = await modelAdapter.generateUXDesignerPrompt(currentPrompt, selectedProvider, selectedModel); + console.log("[UXDesignerPrompt] Generation result:", result); + if (result.success && result.data) { setGeneratedPrompt(result.data); setEnhancedPrompt(result.data); } else { + console.error("[UXDesignerPrompt] Generation failed:", result.error); setError(result.error || "Failed to generate UX designer prompt"); } } catch (err) { + console.error("[UXDesignerPrompt] Generation error:", err); setError(err instanceof Error ? err.message : "An error occurred"); } finally { setProcessing(false); diff --git a/lib/services/adapter-instance.ts b/lib/services/adapter-instance.ts index 7d71065..b3e799c 100644 --- a/lib/services/adapter-instance.ts +++ b/lib/services/adapter-instance.ts @@ -2,4 +2,6 @@ import ModelAdapter from "./model-adapter"; const adapter = new ModelAdapter(); +adapter["qwenService"]["initialize"]?.(); + export default adapter; diff --git a/lib/services/model-adapter.ts b/lib/services/model-adapter.ts index c41e9c6..a50b1d5 100644 --- a/lib/services/model-adapter.ts +++ b/lib/services/model-adapter.ts @@ -70,6 +70,23 @@ export class ModelAdapter { return this.qwenService.getTokenInfo(); } + hasQwenAuth(): boolean { + return this.qwenService.hasOAuthToken(); + } + + private isProviderAuthenticated(provider: ModelProvider): boolean { + switch (provider) { + case "qwen": + return this.hasQwenAuth() || this.qwenService.hasApiKey(); + case "ollama": + return this.ollamaService.hasAuth(); + case "zai": + return this.zaiService.hasAuth(); + default: + return false; + } + } + private buildFallbackProviders(...providers: ModelProvider[]): ModelProvider[] { const seen = new Set(); return providers.filter((provider) => { @@ -85,13 +102,29 @@ export class ModelAdapter { operation: (service: any) => Promise>, providers: ModelProvider[] ): Promise> { + console.log("[ModelAdapter] Attempting providers in order:", providers); + let lastError: string | null = null; + for (const provider of providers) { try { + console.log(`[ModelAdapter] Checking authentication for ${provider}...`); + + if (!this.isProviderAuthenticated(provider)) { + console.log(`[ModelAdapter] Provider ${provider} is not authenticated, skipping`); + continue; + } + let service: any; + console.log(`[ModelAdapter] Trying provider: ${provider}`); + switch (provider) { case "qwen": service = this.qwenService; + console.log("[ModelAdapter] Qwen service:", { + hasApiKey: !!this.qwenService["apiKey"], + hasToken: !!this.qwenService.getTokenInfo()?.accessToken + }); break; case "ollama": service = this.ollamaService; @@ -102,17 +135,30 @@ export class ModelAdapter { } const result = await operation(service); + console.log(`[ModelAdapter] Provider ${provider} result:`, result); + if (result.success) { + console.log(`[ModelAdapter] Success with provider: ${provider}`); return result; } + + if (result.error) { + lastError = result.error; + } } catch (error) { - console.error(`Error with ${provider}:`, error); + const errorMessage = error instanceof Error ? error.message : String(error); + console.error(`[ModelAdapter] Error with ${provider}:`, errorMessage); + lastError = errorMessage || lastError; } } + const finalError = lastError + ? `All providers failed: ${lastError}` + : "All providers failed. Please configure API key in Settings"; + console.error(`[ModelAdapter] ${finalError}`); return { success: false, - error: "All providers failed", + error: finalError, }; } diff --git a/lib/services/ollama-cloud.ts b/lib/services/ollama-cloud.ts index fee6a2e..7c86228 100644 --- a/lib/services/ollama-cloud.ts +++ b/lib/services/ollama-cloud.ts @@ -53,6 +53,10 @@ export class OllamaCloudService { }; } + hasAuth(): boolean { + return !!this.config.apiKey; + } + private ensureApiKey(): string { if (this.config.apiKey) { return this.config.apiKey; diff --git a/lib/services/qwen-oauth.ts b/lib/services/qwen-oauth.ts index 01d586b..bb62788 100644 --- a/lib/services/qwen-oauth.ts +++ b/lib/services/qwen-oauth.ts @@ -68,10 +68,20 @@ export class QwenOAuthService { this.apiKey = apiKey; } + hasApiKey(): boolean { + return !!this.apiKey; + } + + hasOAuthToken(): boolean { + return !!this.getTokenInfo()?.accessToken; + } + /** * Build default headers for Qwen completions (includes OAuth token refresh). */ private async getRequestHeaders(): Promise> { + console.log("[QwenOAuth] Getting request headers..."); + const token = await this.getValidToken(); const headers: Record = { "Content-Type": "application/json", @@ -79,14 +89,17 @@ export class QwenOAuthService { if (token?.accessToken) { headers["Authorization"] = `Bearer ${token.accessToken}`; + console.log("[QwenOAuth] Using OAuth token for authorization"); return headers; } if (this.apiKey) { headers["Authorization"] = `Bearer ${this.apiKey}`; + console.log("[QwenOAuth] Using API key for authorization"); return headers; } + console.error("[QwenOAuth] No OAuth token or API key available"); throw new Error("Please configure a Qwen API key or authenticate via OAuth."); } @@ -96,8 +109,11 @@ export class QwenOAuthService { private getEffectiveEndpoint(): string { const resourceUrl = this.token?.resourceUrl; if (resourceUrl) { - return this.normalizeResourceUrl(resourceUrl); + const normalized = this.normalizeResourceUrl(resourceUrl); + console.log("[Qwen] Using resource URL:", normalized); + return normalized; } + console.log("[Qwen] Using default endpoint:", this.endpoint); return this.endpoint; } @@ -109,7 +125,12 @@ export class QwenOAuthService { const withProtocol = trimmed.startsWith("http") ? trimmed : `https://${trimmed}`; const cleaned = withProtocol.replace(/\/$/, ""); - return cleaned.endsWith("/v1") ? cleaned : `${cleaned}/v1`; + + if (cleaned.endsWith("/v1") || cleaned.endsWith("/compatible-mode/v1")) { + return cleaned; + } + + return `${cleaned}/v1`; } private hydrateTokens() { @@ -132,6 +153,7 @@ export class QwenOAuthService { private getStoredToken(): QwenOAuthToken | null { this.hydrateTokens(); + console.log("[QwenOAuth] Retrieved stored token:", this.token ? { hasAccessToken: !!this.token.accessToken, expiresAt: this.token.expiresAt } : null); return this.token; } @@ -229,8 +251,18 @@ export class QwenOAuthService { this.storageHydrated = true; } + /** + * Initialize the service and hydrate tokens from storage. + */ + initialize(): void { + console.log("[QwenOAuth] Initializing service..."); + this.hydrateTokens(); + } + getTokenInfo(): QwenOAuthToken | null { - return this.getStoredToken(); + this.hydrateTokens(); + console.log("[QwenOAuth] getTokenInfo called, returning:", this.token ? { hasAccessToken: !!this.token.accessToken, expiresAt: this.token.expiresAt } : null); + return this.token; } /** @@ -353,12 +385,30 @@ export class QwenOAuthService { } private parseTokenResponse(data: any): QwenOAuthToken { - return { + console.log("[QwenOAuth] Token response received:", data); + + const token: QwenOAuthToken = { accessToken: data.access_token, refreshToken: data.refresh_token, - resourceUrl: data.resource_url, expiresAt: data.expires_in ? Date.now() + data.expires_in * 1000 : undefined, }; + + if (data.resource_url) { + token.resourceUrl = data.resource_url; + console.log("[QwenOAuth] Using resource_url from response:", data.resource_url); + } else if (data.endpoint) { + token.resourceUrl = data.endpoint; + console.log("[QwenOAuth] Using endpoint from response:", data.endpoint); + } else if (data.resource_server) { + token.resourceUrl = `https://${data.resource_server}/compatible-mode/v1`; + console.log("[QwenOAuth] Using resource_server from response:", data.resource_server); + } else { + console.log("[QwenOAuth] No resource_url/endpoint in response, will use default Qwen endpoint"); + console.log("[QwenOAuth] Available fields in response:", Object.keys(data)); + } + + console.log("[QwenOAuth] Parsed token:", { hasAccessToken: !!token.accessToken, hasRefreshToken: !!token.refreshToken, hasResourceUrl: !!token.resourceUrl, expiresAt: token.expiresAt }); + return token; } /** @@ -395,12 +445,19 @@ export class QwenOAuthService { ): Promise> { try { const headers = await this.getRequestHeaders(); - const url = `${this.getEffectiveEndpoint()}/chat/completions`; + const baseUrl = this.getEffectiveEndpoint(); + const url = `${this.oauthBaseUrl}/chat`; + + console.log("[Qwen] Chat completion request:", { url, model, hasAuth: !!headers.Authorization }); const response = await fetch(url, { method: "POST", - headers, + headers: { + "Content-Type": "application/json", + Authorization: headers.Authorization || "", + }, body: JSON.stringify({ + endpoint: baseUrl, model, messages, stream, @@ -409,6 +466,7 @@ export class QwenOAuthService { if (!response.ok) { const errorText = await response.text(); + console.error("[Qwen] Chat completion failed:", response.status, response.statusText, errorText); throw new Error(`Chat completion failed (${response.status}): ${response.statusText} - ${errorText}`); } diff --git a/lib/services/zai-plan.ts b/lib/services/zai-plan.ts index ca74369..d742c3c 100644 --- a/lib/services/zai-plan.ts +++ b/lib/services/zai-plan.ts @@ -17,6 +17,10 @@ export class ZaiPlanService { }; } + hasAuth(): boolean { + return !!this.config.apiKey; + } + private getHeaders(): Record { return { "Content-Type": "application/json", diff --git a/package-lock.json b/package-lock.json index 5cfd9cf..fdb2d81 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "eslint": "^9.16.0", "eslint-config-next": "^15.0.3", "lucide-react": "^0.562.0", - "next": "^15.0.3", + "next": "^16.1.1", "postcss": "^8.4.49", "react": "^19.0.0", "react-dom": "^19.0.0", @@ -50,17 +50,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@emnapi/core": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", - "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" - } - }, "node_modules/@emnapi/runtime": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", @@ -71,16 +60,6 @@ "tslib": "^2.4.0" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", @@ -764,22 +743,10 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" - } - }, "node_modules/@next/env": { - "version": "15.5.9", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.9.tgz", - "integrity": "sha512-4GlTZ+EJM7WaW2HEZcyU317tIQDjkQIyENDLxYJfSWlfqguN+dHkZgyQTV/7ykvobU7yEH5gKvreNrH4B6QgIg==", + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.1.tgz", + "integrity": "sha512-3oxyM97Sr2PqiVyMyrZUtrtM3jqqFxOQJVuKclDsgj/L728iZt/GyslkN4NwarledZATCenbk4Offjk1hQmaAA==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -792,9 +759,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.7.tgz", - "integrity": "sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw==", + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.1.tgz", + "integrity": "sha512-JS3m42ifsVSJjSTzh27nW+Igfha3NdBOFScr9C80hHGrWx55pTrVL23RJbqir7k7/15SKlrLHhh/MQzqBBYrQA==", "cpu": [ "arm64" ], @@ -808,9 +775,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.7.tgz", - "integrity": "sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg==", + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.1.tgz", + "integrity": "sha512-hbyKtrDGUkgkyQi1m1IyD3q4I/3m9ngr+V93z4oKHrPcmxwNL5iMWORvLSGAf2YujL+6HxgVvZuCYZfLfb4bGw==", "cpu": [ "x64" ], @@ -824,9 +791,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.7.tgz", - "integrity": "sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA==", + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.1.tgz", + "integrity": "sha512-/fvHet+EYckFvRLQ0jPHJCUI5/B56+2DpI1xDSvi80r/3Ez+Eaa2Yq4tJcRTaB1kqj/HrYKn8Yplm9bNoMJpwQ==", "cpu": [ "arm64" ], @@ -840,9 +807,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.7.tgz", - "integrity": "sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw==", + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.1.tgz", + "integrity": "sha512-MFHrgL4TXNQbBPzkKKur4Fb5ICEJa87HM7fczFs2+HWblM7mMLdco3dvyTI+QmLBU9xgns/EeeINSZD6Ar+oLg==", "cpu": [ "arm64" ], @@ -856,9 +823,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.7.tgz", - "integrity": "sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw==", + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.1.tgz", + "integrity": "sha512-20bYDfgOQAPUkkKBnyP9PTuHiJGM7HzNBbuqmD0jiFVZ0aOldz+VnJhbxzjcSabYsnNjMPsE0cyzEudpYxsrUQ==", "cpu": [ "x64" ], @@ -872,9 +839,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.7.tgz", - "integrity": "sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA==", + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.1.tgz", + "integrity": "sha512-9pRbK3M4asAHQRkwaXwu601oPZHghuSC8IXNENgbBSyImHv/zY4K5udBusgdHkvJ/Tcr96jJwQYOll0qU8+fPA==", "cpu": [ "x64" ], @@ -888,9 +855,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.7.tgz", - "integrity": "sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ==", + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.1.tgz", + "integrity": "sha512-bdfQkggaLgnmYrFkSQfsHfOhk/mCYmjnrbRCGgkMcoOBZ4n+TRRSLmT/CU5SATzlBJ9TpioUyBW/vWFXTqQRiA==", "cpu": [ "arm64" ], @@ -904,9 +871,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.7.tgz", - "integrity": "sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw==", + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.1.tgz", + "integrity": "sha512-Ncwbw2WJ57Al5OX0k4chM68DKhEPlrXBaSXDCi2kPi5f4d8b3ejr3RRJGfKBLrn2YJL5ezNS7w2TZLHSti8CMw==", "cpu": [ "x64" ], @@ -1032,16 +999,6 @@ "tslib": "^2.8.0" } }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/d3-array": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", @@ -1467,243 +1424,6 @@ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "license": "ISC" }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", - "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", - "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", - "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", - "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", - "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", - "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", - "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", - "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", - "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", - "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", - "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", - "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", - "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", - "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@unrs/resolver-binding-win32-x64-msvc": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", @@ -3499,20 +3219,6 @@ "url": "https://github.com/sponsors/rawify" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -5560,13 +5266,14 @@ "license": "MIT" }, "node_modules/next": { - "version": "15.5.9", - "resolved": "https://registry.npmjs.org/next/-/next-15.5.9.tgz", - "integrity": "sha512-agNLK89seZEtC5zUHwtut0+tNrc0Xw4FT/Dg+B/VLEo9pAcS9rtTKpek3V6kVcVwsB2YlqMaHdfZL4eLEVYuCg==", + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/next/-/next-16.1.1.tgz", + "integrity": "sha512-QI+T7xrxt1pF6SQ/JYFz95ro/mg/1Znk5vBebsWwbpejj1T0A23hO7GYEaVac9QUOT2BIMiuzm0L99ooq7k0/w==", "license": "MIT", "dependencies": { - "@next/env": "15.5.9", + "@next/env": "16.1.1", "@swc/helpers": "0.5.15", + "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" @@ -5575,18 +5282,18 @@ "next": "dist/bin/next" }, "engines": { - "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.5.7", - "@next/swc-darwin-x64": "15.5.7", - "@next/swc-linux-arm64-gnu": "15.5.7", - "@next/swc-linux-arm64-musl": "15.5.7", - "@next/swc-linux-x64-gnu": "15.5.7", - "@next/swc-linux-x64-musl": "15.5.7", - "@next/swc-win32-arm64-msvc": "15.5.7", - "@next/swc-win32-x64-msvc": "15.5.7", - "sharp": "^0.34.3" + "@next/swc-darwin-arm64": "16.1.1", + "@next/swc-darwin-x64": "16.1.1", + "@next/swc-linux-arm64-gnu": "16.1.1", + "@next/swc-linux-arm64-musl": "16.1.1", + "@next/swc-linux-x64-gnu": "16.1.1", + "@next/swc-linux-x64-musl": "16.1.1", + "@next/swc-win32-arm64-msvc": "16.1.1", + "@next/swc-win32-x64-msvc": "16.1.1", + "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", diff --git a/package.json b/package.json index 187f7d7..8f58dbe 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "eslint": "^9.16.0", "eslint-config-next": "^15.0.3", "lucide-react": "^0.562.0", - "next": "^15.0.3", + "next": "^16.1.1", "postcss": "^8.4.49", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/tsconfig.json b/tsconfig.json index d8b9323..e7ff3a2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -11,7 +15,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve", + "jsx": "react-jsx", "incremental": true, "plugins": [ { @@ -19,9 +23,19 @@ } ], "paths": { - "@/*": ["./*"] + "@/*": [ + "./*" + ] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] }