Files
agent-desktop/console/vite.config.ts
T
Pine 6a87496a4b fix: 统一桌面端登录与 token 管理逻辑,修复模型列表 401
- PineAgents provider base_url 改为动态使用 DEMO_API_BASE_URL(远程服务器)
- list_provider_info 支持从请求头透传 console 端 JWT,优先用于 /v1/models
- forward/forward_upload 未显式传 auth_header 时自动使用本地持久化 token
- auth_token_store 添加日志,便于排查 token 保存/加载问题
- fetch_models 超时增加到 15s,添加获取结果日志
2026-09-02 18:18:55 +08:00

234 lines
8.7 KiB
TypeScript

/// <reference types="vitest" />
import { defineConfig, loadEnv } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";
// Vitest plugin: transforms .css imports inside node_modules to empty stubs.
// This prevents errors from packages like @agentscope-ai/icons that import CSS.
const cssStubPlugin = {
name: "css-stub",
transform(_code: string, id: string) {
if (id.includes("node_modules") && id.endsWith(".css")) {
return { code: "export default {}" };
}
},
};
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), "");
// Empty = same-origin; frontend and backend served together, no hardcoded host.
// Use a dedicated Vite-prefixed key so unrelated shell BASE_URL values don't leak into the build.
const apiBaseUrl = env.VITE_API_BASE_URL ?? "";
return {
define: {
VITE_API_BASE_URL: JSON.stringify(apiBaseUrl),
TOKEN: JSON.stringify(env.TOKEN || ""),
MOBILE: false,
},
plugins: [react(), cssStubPlugin],
css: {
modules: {
localsConvention: "camelCase",
generateScopedName: "[name]__[local]__[hash:base64:5]",
},
preprocessorOptions: {
less: {
javascriptEnabled: true,
},
},
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
server: {
host: "0.0.0.0",
port: 5173,
proxy: {
"/api": {
target: "http://localhost:8088",
changeOrigin: false,
},
},
},
test: {
globals: true,
environment: "jsdom",
setupFiles: ["./src/test/setup.ts"],
css: true,
// all @agentscope-ai/* packages excluded from inline — they are large / have CSS imports
// aliases below redirect each to a stub or compiled entry
deps: {
inline: [/@agentscope-ai\/(?!icons|chat|design)/],
},
alias: {
// chat is aliased to a tiny stub to avoid OOM from the 2.3MB real package
// Tests that need specific behavior override with vi.mock('@agentscope-ai/chat', factory)
"@agentscope-ai/chat": path.resolve(__dirname, "src/test/chat-mock.ts"),
// design is aliased to a stub to avoid hanging from its 3MB lib
"@agentscope-ai/design": path.resolve(
__dirname,
"src/test/design-mock.ts",
),
"@agentscope-ai/icons": path.resolve(
__dirname,
"src/test/icons-mock.ts",
),
"@tauri-apps/api/core": path.resolve(
__dirname,
"src/test/tauri-mock.ts",
),
"@tauri-apps/plugin-dialog": path.resolve(
__dirname,
"src/test/tauri-mock.ts",
),
},
exclude: [
"**/node_modules/**",
"**/dist/**",
// 旧测试用 node:test,与 vitest 不兼容,待迁移
"**/testConnectionMessage.test.ts",
// ChatPage test causes worker crash - pre-existing issue, needs more mock setup
"**/pages/Chat/ChatPage.test.tsx",
// Tauri modules require @tauri-apps/api which only exists in desktop builds
"**/src/tauri/**",
],
coverage: {
provider: "v8",
reporter: ["text", "html", "json", "json-summary", "lcov", "cobertura"],
include: ["src/**/*.{ts,tsx}"],
exclude: [
"src/test/**",
"src/tauri/**",
"src/**/*.d.ts",
"src/main.tsx",
"src/vite-env.d.ts",
],
thresholds: {
statements: 5,
branches: 4,
functions: 3,
lines: 5,
},
},
},
optimizeDeps: {
include: ["diff"],
// @agentscope-ai/design 的 FileIcon 组件用 import 导入 SVG(用作 <img src>)。
// 预构建时 esbuild 默认不识别 .svg 扩展名,会报错 "No loader is configured"。
// 配置 file loader:把 SVG 复制到预构建输出目录并返回 URL 路径,
// 与 FileIcon 的 <img src={maps[type]}> 用法匹配。
// 注意:不能用 optimizeDeps.exclude 排除整个库,那会导致它和所有
// CommonJS 依赖(classnames/react-is/rc-util 等)的 interop 失效,
// 出现无穷无尽的 "does not provide an export named 'default'" 错误。
esbuildOptions: {
loader: {
".svg": "file",
},
},
},
build: {
// Output to QwenPaw's console directory,
// so we don't need to copy files manually after build.
// outDir: path.resolve(__dirname, "../src/pineagents/console"),
// emptyOutDir: true,
cssCodeSplit: true,
sourcemap: mode !== "production",
// Warn only for chunks above 9MB. The largest chunk is ui-vendor
// (antd + @agentscope-ai merged, ~8.4MB) which MUST stay a single chunk
// to avoid circular chunk graphs. The other large chunks are:
// • ts.worker ~7MB — Monaco TS language-service worker (loaded on demand)
// • index ~5.6MB — app shell + deps shared by lazy route chunks.
// A 1MB default would warn on these forever; 9MB still surfaces any
// genuinely out-of-control new chunk.
chunkSizeWarningLimit: 9000,
rollupOptions: {
onwarn(warning, warn) {
// Suppress the "dynamic import will not move module into another
// chunk" noise. These fire because src/pages modules are BOTH:
// • statically imported — page components legitimately share
// sub-components (MarketPanel, DetailDrawer, providerIcon, …),
// • dynamically imported — via dynamicModuleRegistry's
// import.meta.glob (plugin patch registry) and lazyWithRetry's
// PAGE_MODULES glob (route code-splitting).
// When a module is already statically reachable, Rollup simply cannot
// give it its own chunk; the dynamic import silently reuses the
// static one. That is intended behaviour here, not an error — the
// registry glob is what powers window.QwenPaw.modules patching.
if (
warning.message?.includes(
"dynamic import will not move module into another chunk",
)
) {
return;
}
warn(warning);
},
output: {
manualChunks(id) {
// React core
if (
id.includes("node_modules/react/") ||
id.includes("node_modules/react-dom/") ||
id.includes("node_modules/react-router-dom/") ||
id.includes("node_modules/scheduler/")
) {
return "react-vendor";
}
// Ant Design + AgentScope design system MUST stay in ONE chunk.
// antd and @agentscope-ai/design share react-is / rc-util and import
// each other; splitting them into separate vendor chunks produces a
// circular chunk graph (ui-vendor ↔ agentscope-vendor) that breaks
// ES-module init ordering and throws "Cannot access X before
// initialization" at runtime (white screen). Merged here to avoid
// those circular deps — the tradeoff is a larger single vendor chunk.
if (
id.includes("node_modules/antd/") ||
id.includes("node_modules/antd-style/") ||
id.includes("node_modules/@ant-design/") ||
id.includes("node_modules/@agentscope-ai/")
) {
return "ui-vendor";
}
// i18n
if (
id.includes("node_modules/i18next/") ||
id.includes("node_modules/react-i18next/")
) {
return "i18n-vendor";
}
// Markdown rendering
if (
id.includes("node_modules/react-markdown/") ||
id.includes("node_modules/remark-gfm/") ||
id.includes("node_modules/rehype") ||
id.includes("node_modules/remark") ||
id.includes("node_modules/unified/") ||
id.includes("node_modules/mdast") ||
id.includes("node_modules/hast") ||
id.includes("node_modules/micromark")
) {
return "markdown-vendor";
}
// Drag and drop
if (id.includes("node_modules/@dnd-kit/")) {
return "dnd-vendor";
}
// Utilities (dayjs, zustand, ahooks, etc.)
if (
id.includes("node_modules/dayjs/") ||
id.includes("node_modules/zustand/") ||
id.includes("node_modules/ahooks/") ||
id.includes("node_modules/@vvo/tzdb/")
) {
return "utils-vendor";
}
},
},
},
},
};
});