import { readFileSync } from 'node:fs' import { resolve } from 'node:path' import react from '@vitejs/plugin-react' import { defineConfig, externalizeDepsPlugin } from 'electron-vite' import type { Plugin } from 'vite' import { packageObsidianMcpVault } from './src/main/obsidian/package-mcpvault' type ProjectPackage = { dependencies?: Record } const projectPackage = JSON.parse( readFileSync(resolve('package.json'), 'utf8') ) as ProjectPackage function requireDependencyVersion( dependencies: Record | undefined, name: string ): string { const version = dependencies?.[name] if (!version) { throw new Error(`Missing ${name} dependency version`) } return version } const deepSeekHarnessLlmVersion = requireDependencyVersion( projectPackage.dependencies, '@deepseek-ai/dsh-llm' ) export function serializeDeepSeekHarnessBundleManifest( version: string ): string { return `${JSON.stringify( { name: '@deepseek-ai/dsh-llm', version, private: true, type: 'module' }, null, 2 )}\n` } function deepSeekHarnessBundleManifestPlugin(): Plugin { return { name: 'deepseek-harness-bundle-manifest', generateBundle() { this.emitFile({ type: 'asset', fileName: 'package.json', source: serializeDeepSeekHarnessBundleManifest( deepSeekHarnessLlmVersion ) }) } } } export function stableMainEntryFileName(chunk: { readonly name: string }): string { if (chunk.name === 'remote-package-installer') { return 'remote-package-installer.mjs' } return [ 'deepseek-harness-host-bootstrap', 'embedding-inference-bootstrap' ].includes(chunk.name) ? `${chunk.name}.js` : '[name].js' } // The renderer CSP has no font-src, so fonts fall back to default-src 'self'. // Small fonts (e.g. KaTeX) must stay files rather than data: URLs, or Chromium // refuses them. Other assets keep Vite's default inline threshold. export function rendererAssetInlineLimit( filePath: string ): false | undefined { return /\.(?:woff2?|ttf|otf)(?:$|\?)/iu.test(filePath) ? false : undefined } export default defineConfig({ main: { plugins: [ externalizeDepsPlugin({ exclude: [ '@agentclientprotocol/sdk', '@deepseek-ai/cordis', '@deepseek-ai/dsh-agent', '@deepseek-ai/dsh-agent-loop', '@deepseek-ai/dsh-bash-local', '@deepseek-ai/dsh-credentials', '@deepseek-ai/dsh-fs-local', '@deepseek-ai/dsh-llm', '@deepseek-ai/dsh-llm-pi-ai', '@deepseek-ai/dsh-pwsh-local', '@deepseek-ai/dsh-sandbox', '@deepseek-ai/dsh-sandbox-policy', '@deepseek-ai/dsh-session', '@deepseek-ai/dsh-session-projection', '@deepseek-ai/dsh-shell-env', '@deepseek-ai/dsh-skill', '@deepseek-ai/dsh-subprocess-local', '@deepseek-ai/dsh-system-prompt', '@deepseek-ai/dsh-token-meter', '@deepseek-ai/dsh-tool-bash', '@deepseek-ai/dsh-tool-fs', '@deepseek-ai/dsh-tool-pwsh', '@deepseek-ai/dsh-tool-skill', '@deepseek-ai/dsh-tools', '@deepseek-ai/dsh-user-approval', 'yaml', 'zod' ] }), deepSeekHarnessBundleManifestPlugin(), { name: 'obsidian-mcpvault-package', async writeBundle(options) { await packageObsidianMcpVault( resolve('.'), resolve(options.dir ?? 'out/main', 'obsidian-mcpvault') ) } } ], build: { rollupOptions: { input: { index: resolve('src/main/index.ts'), 'assistant-storage-worker': resolve('src/main/assistant-storage-worker.ts'), 'execution-stats-worker': resolve('src/main/execution-stats-worker.ts'), 'readonly-query-worker': resolve('src/main/readonly-query-worker.ts'), 'document-parse-worker': resolve('src/main/document-parse-worker.ts'), 'wechat-sidecar': resolve( 'src/main/channels/wechat-sidecar.ts' ), 'deepseek-harness-host-bootstrap': resolve( 'src/main/deepseek-harness-host-bootstrap.ts' ), 'embedding-inference-bootstrap': resolve( 'src/main/embedding-inference-bootstrap.ts' ), 'remote-package-installer': resolve( 'src/main/remote-agent/control-plane-package-installer-cli.ts' ) }, external: [ 'node-pty', 'koffi', /^@koromix\/koffi-/u ], output: { entryFileNames(chunk) { return stableMainEntryFileName(chunk) }, chunkFileNames(chunk) { const moduleIds = chunk.moduleIds.join('\n') return moduleIds.includes('deepseek-harness') || moduleIds.includes('deepseek-harness-utility') ? 'chunks/deepseek-harness-[name]-[hash].js' : 'chunks/[name]-[hash].js' }, manualChunks(id) { if ( id.includes('@deepseek-ai/dsh-llm') || id.includes('@deepseek-ai/dsh-credentials') || id.includes('@deepseek-ai/dsh-settings') || id.includes('@deepseek-ai/dsh-timeout') || id.includes('@deepseek-ai/dsh-token-meter') || id.includes('@deepseek-ai/dsh-llm-pi-ai') || id.includes('@mariozechner/pi-ai') ) { return 'deepseek-harness-llm' } return undefined } } } } }, preload: { plugins: [externalizeDepsPlugin()], build: { rollupOptions: { output: { entryFileNames: '[name].cjs', format: 'cjs' } } } }, renderer: { resolve: { alias: { '@renderer': resolve('src/renderer/src'), '@shared': resolve('src/shared') } }, worker: { format: 'es' }, build: { assetsInlineLimit: rendererAssetInlineLimit }, plugins: [react()] } })