From 33b797d12505f201f14e35f714863d1e58d1c130 Mon Sep 17 00:00:00 2001 From: apeltekci Date: Fri, 6 Mar 2026 18:28:55 -0800 Subject: [PATCH 1/3] fix(version): stop dashboard from using npm CLI updates --- src/app/api/version/route.js | 20 ++++++++++++++++++-- src/shared/components/Sidebar.js | 10 ++++++---- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/app/api/version/route.js b/src/app/api/version/route.js index 7e7f4a80..3376beb4 100644 --- a/src/app/api/version/route.js +++ b/src/app/api/version/route.js @@ -2,6 +2,7 @@ import https from "https"; import pkg from "../../../../package.json" with { type: "json" }; const NPM_PACKAGE_NAME = "9router"; +const ENABLE_NPM_UPDATE_CHECK = process.env.ENABLE_NPM_UPDATE_CHECK === "true"; // Fetch latest version from npm registry function fetchLatestVersion() { @@ -37,9 +38,24 @@ function compareVersions(a, b) { } export async function GET() { - const latestVersion = await fetchLatestVersion(); const currentVersion = pkg.version; + if (!ENABLE_NPM_UPDATE_CHECK) { + return Response.json({ + currentVersion, + latestVersion: currentVersion, + hasUpdate: false, + source: "app" + }); + } + + const latestVersion = await fetchLatestVersion(); const hasUpdate = latestVersion ? compareVersions(latestVersion, currentVersion) > 0 : false; - return Response.json({ currentVersion, latestVersion, hasUpdate }); + return Response.json({ + currentVersion, + latestVersion: latestVersion || currentVersion, + hasUpdate, + source: "npm", + installCommand: hasUpdate ? `npm install -g ${NPM_PACKAGE_NAME}@latest` : null + }); } diff --git a/src/shared/components/Sidebar.js b/src/shared/components/Sidebar.js index 8a35fe61..7c10e57a 100644 --- a/src/shared/components/Sidebar.js +++ b/src/shared/components/Sidebar.js @@ -42,7 +42,7 @@ export default function Sidebar({ onClose }) { .catch(() => {}); }, []); - // Lazy check for new npm version on mount + // Check for update metadata on mount. useEffect(() => { fetch("/api/version") .then(res => res.json()) @@ -97,9 +97,11 @@ export default function Sidebar({ onClose }) { ↑ New version available: v{updateInfo.latestVersion} - - npm install -g 9router@latest - + {updateInfo.installCommand && ( + + {updateInfo.installCommand} + + )} )} From b80111c1662c9fa89bdd4534b2fd6bf74698fdf0 Mon Sep 17 00:00:00 2001 From: apeltekci Date: Fri, 6 Mar 2026 22:16:59 -0800 Subject: [PATCH 2/3] fix(cline): use workos auth token shape --- open-sse/executors/default.js | 3 ++ open-sse/services/provider.js | 5 +++ src/app/api/providers/[id]/test/testUtils.js | 18 +++------- src/shared/utils/clineAuth.js | 37 ++++++++++++++++++++ 4 files changed, 50 insertions(+), 13 deletions(-) create mode 100644 src/shared/utils/clineAuth.js diff --git a/open-sse/executors/default.js b/open-sse/executors/default.js index a47da862..eba3c61f 100644 --- a/open-sse/executors/default.js +++ b/open-sse/executors/default.js @@ -1,5 +1,6 @@ import { BaseExecutor } from "./base.js"; import { PROVIDERS, OAUTH_ENDPOINTS } from "../config/constants.js"; +import { buildClineHeaders } from "../../src/shared/utils/clineAuth.js"; export class DefaultExecutor extends BaseExecutor { constructor(provider) { @@ -65,6 +66,8 @@ export class DefaultExecutor extends BaseExecutor { if (credentials.providerSpecificData?.orgId) { headers["X-Kilocode-OrganizationID"] = credentials.providerSpecificData.orgId; } + } else if (this.provider === "cline") { + Object.assign(headers, buildClineHeaders(credentials.apiKey || credentials.accessToken)); } else { headers["Authorization"] = `Bearer ${credentials.apiKey || credentials.accessToken}`; } diff --git a/open-sse/services/provider.js b/open-sse/services/provider.js index 42931ec1..93101367 100644 --- a/open-sse/services/provider.js +++ b/open-sse/services/provider.js @@ -1,4 +1,5 @@ import { PROVIDERS } from "../config/constants.js"; +import { buildClineHeaders } from "../../src/shared/utils/clineAuth.js"; const OPENAI_COMPATIBLE_PREFIX = "openai-compatible-"; const OPENAI_COMPATIBLE_DEFAULTS = { @@ -285,6 +286,10 @@ export function buildProviderHeaders(provider, credentials, stream = true, body case "openrouter": headers["Authorization"] = `Bearer ${credentials.apiKey || credentials.accessToken}`; break; + + case "cline": + Object.assign(headers, buildClineHeaders(credentials.apiKey || credentials.accessToken)); + break; case "glm": case "kimi": diff --git a/src/app/api/providers/[id]/test/testUtils.js b/src/app/api/providers/[id]/test/testUtils.js index 45c673ee..f299ae07 100644 --- a/src/app/api/providers/[id]/test/testUtils.js +++ b/src/app/api/providers/[id]/test/testUtils.js @@ -11,6 +11,7 @@ import { CLINE_CONFIG, KILOCODE_CONFIG, } from "@/lib/oauth/constants/oauth"; +import { buildClineHeaders } from "@/shared/utils/clineAuth"; // OAuth provider test endpoints const OAUTH_TEST_CONFIG = { @@ -56,19 +57,10 @@ const OAUTH_TEST_CONFIG = { }; async function probeClineAccessToken(accessToken) { - const res = await fetch("https://api.cline.bot/api/v1/chat/completions", { - method: "POST", - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json", - "HTTP-Referer": "https://cline.bot", - "X-Title": "Cline", - }, - body: JSON.stringify({ - model: "cl/anthropic/claude-sonnet-4-20250514", - messages: [{ role: "user", content: "test" }], - max_tokens: 1, - stream: false, + const res = await fetch("https://api.cline.bot/api/v1/users/me", { + method: "GET", + headers: buildClineHeaders(accessToken, { + Accept: "application/json", }), }); diff --git a/src/shared/utils/clineAuth.js b/src/shared/utils/clineAuth.js new file mode 100644 index 00000000..a2da21dd --- /dev/null +++ b/src/shared/utils/clineAuth.js @@ -0,0 +1,37 @@ +import pkg from "../../../package.json" with { type: "json" }; + +const APP_VERSION = pkg.version || "0.0.0"; + +export function getClineAccessToken(token) { + if (typeof token !== "string") return ""; + const trimmed = token.trim(); + if (!trimmed) return ""; + return trimmed.startsWith("workos:") ? trimmed : `workos:${trimmed}`; +} + +export function getClineAuthorizationHeader(token) { + const accessToken = getClineAccessToken(token); + return accessToken ? `Bearer ${accessToken}` : ""; +} + +export function buildClineHeaders(token, extraHeaders = {}) { + const authorization = getClineAuthorizationHeader(token); + const headers = { + "HTTP-Referer": "https://cline.bot", + "X-Title": "Cline", + "User-Agent": `9Router/${APP_VERSION}`, + "X-PLATFORM": process.platform || "unknown", + "X-PLATFORM-VERSION": process.version || "unknown", + "X-CLIENT-TYPE": "9router", + "X-CLIENT-VERSION": APP_VERSION, + "X-CORE-VERSION": APP_VERSION, + "X-IS-MULTIROOT": "false", + ...extraHeaders, + }; + + if (authorization) { + headers.Authorization = authorization; + } + + return headers; +} From 0400bcc308820d8bd8a5f8fd22ec7cfffabbe94e Mon Sep 17 00:00:00 2001 From: apeltekci Date: Fri, 6 Mar 2026 22:20:58 -0800 Subject: [PATCH 3/3] fix(cline): refresh static model catalog --- open-sse/config/providerModels.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/open-sse/config/providerModels.js b/open-sse/config/providerModels.js index 85b1099a..32e8131a 100644 --- a/open-sse/config/providerModels.js +++ b/open-sse/config/providerModels.js @@ -135,13 +135,13 @@ export const PROVIDER_MODELS = { { id: "deepseek/deepseek-reasoner", name: "DeepSeek Reasoner" }, ], cl: [ // Cline - { id: "anthropic/claude-sonnet-4-20250514", name: "Claude Sonnet 4" }, - { id: "anthropic/claude-opus-4-20250514", name: "Claude Opus 4" }, - { id: "google/gemini-2.5-pro", name: "Gemini 2.5 Pro" }, - { id: "google/gemini-2.5-flash", name: "Gemini 2.5 Flash" }, - { id: "openai/gpt-4.1", name: "GPT-4.1" }, - { id: "openai/o3", name: "o3" }, - { id: "deepseek/deepseek-chat", name: "DeepSeek Chat" }, + { id: "anthropic/claude-sonnet-4.6", name: "Claude Sonnet 4.6" }, + { id: "anthropic/claude-opus-4.6", name: "Claude Opus 4.6" }, + { id: "openai/gpt-5.3-codex", name: "GPT-5.3 Codex" }, + { id: "openai/gpt-5.4", name: "GPT-5.4" }, + { id: "google/gemini-3.1-pro-preview", name: "Gemini 3.1 Pro Preview" }, + { id: "google/gemini-3.1-flash-lite-preview", name: "Gemini 3.1 Flash Lite Preview" }, + { id: "kwaipilot/kat-coder-pro", name: "KAT Coder Pro" }, ], // API Key Providers (alias = id) @@ -372,4 +372,3 @@ export function getModelsByProviderId(providerId) { const alias = PROVIDER_ID_TO_ALIAS[providerId] || providerId; return PROVIDER_MODELS[alias] || []; } -