diff --git a/next.config.mjs b/next.config.mjs index 31644a31..8e848dd2 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -2,10 +2,18 @@ const nextConfig = { output: "standalone", serverExternalPackages: ["better-sqlite3"], + outputFileTracingExcludes: { + "*": [ + "**/Cookies/**", + "**/AppData/Local/**", + "**/node_modules/.cache/**", + ], + }, images: { unoptimized: true }, env: {}, + turbopack: {}, webpack: (config, { isServer }) => { // Ignore fs/path modules in browser bundle if (!isServer) { diff --git a/package.json b/package.json index c21df767..7944a2dc 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "private": true, "scripts": { "dev": "next dev --webpack --port 20128", - "build": "NODE_ENV=production next build --webpack", - "start": "NODE_ENV=production next start", + "build": "cross-env NODE_ENV=production next build", + "start": "cross-env NODE_ENV=production next start", "dev:bun": "bun --bun next dev --webpack --port 20128", "build:bun": "NODE_ENV=production bun --bun next build --webpack", "start:bun": "NODE_ENV=production bun ./.next/standalone/server.js" @@ -44,6 +44,7 @@ }, "devDependencies": { "@tailwindcss/postcss": "^4.1.18", + "cross-env": "^10.1.0", "eslint": "^9", "eslint-config-next": "16.1.6", "postcss": "^8.5.6", diff --git a/src/app/(dashboard)/dashboard/providers/[id]/page.js b/src/app/(dashboard)/dashboard/providers/[id]/page.js index 56aaa12b..f64ccd45 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/page.js +++ b/src/app/(dashboard)/dashboard/providers/[id]/page.js @@ -38,6 +38,7 @@ export default function ProviderDetailPage() { const [providerStrategy, setProviderStrategy] = useState(null); // null = use global, "round-robin" = override const [providerStickyLimit, setProviderStickyLimit] = useState(""); const [suggestedModels, setSuggestedModels] = useState([]); + const [kiloFreeModels, setKiloFreeModels] = useState([]); const { copied, copy } = useCopyToClipboard(); const providerInfo = providerNode @@ -77,6 +78,15 @@ export default function ProviderDetailPage() { } }, []); + // Fetch free models from Kilo API for kilocode provider + useEffect(() => { + if (providerId !== "kilocode") return; + fetch("/api/providers/kilo/free-models") + .then((res) => res.json()) + .then((data) => { if (data.models?.length) setKiloFreeModels(data.models); }) + .catch(() => {}); + }, [providerId]); + const fetchConnections = useCallback(async () => { try { const [connectionsRes, nodesRes, proxyPoolsRes, settingsRes] = await Promise.all([ @@ -537,6 +547,11 @@ export default function ProviderDetailPage() { /> ); } + // Combine hardcoded models with Kilo free models (deduplicated) + const displayModels = [ + ...models, + ...kiloFreeModels.filter((fm) => !models.some((m) => m.id === fm.id)), + ]; // Custom models added by user (stored as aliases: modelId → providerAlias/modelId) const customModels = Object.entries(modelAliases) .filter(([alias, fullModel]) => { @@ -556,7 +571,7 @@ export default function ProviderDetailPage() { return (
- {models.map((model) => { + {displayModels.map((model) => { const fullModel = `${providerStorageAlias}/${model.id}`; const oldFormatModel = `${providerId}/${model.id}`; const existingAlias = Object.entries(modelAliases).find( @@ -575,6 +590,7 @@ export default function ProviderDetailPage() { testStatus={modelTestResults[model.id]} onTest={connections.length > 0 ? () => handleTestModel(model.id) : undefined} isTesting={testingModelId === model.id} + isFree={model.isFree} /> ); })} @@ -964,7 +980,7 @@ export default function ProviderDetailPage() { ); } -function ModelRow({ model, fullModel, alias, copied, onCopy, testStatus, isCustom, onDeleteAlias, onTest, isTesting }) { +function ModelRow({ model, fullModel, alias, copied, onCopy, testStatus, isCustom, isFree, onDeleteAlias, onTest, isTesting }) { const borderColor = testStatus === "ok" ? "border-green-500/40" : testStatus === "error" @@ -1040,6 +1056,7 @@ ModelRow.propTypes = { onCopy: PropTypes.func.isRequired, testStatus: PropTypes.oneOf(["ok", "error"]), isCustom: PropTypes.bool, + isFree: PropTypes.bool, onDeleteAlias: PropTypes.func, onTest: PropTypes.func, isTesting: PropTypes.bool, @@ -1167,7 +1184,10 @@ function PassthroughModelRow({ modelId, fullModel, copied, onCopy, onDeleteAlias

{modelId}

- {fullModel} + {fullModel} + {isFree && ( + Free + )}