diff --git a/ui/desktop/forge.config.ts b/ui/desktop/forge.config.ts index 4c3a77401896..b0c2b69c1838 100644 --- a/ui/desktop/forge.config.ts +++ b/ui/desktop/forge.config.ts @@ -7,7 +7,7 @@ const isLinuxVulkanBuild = process.env.GOOSE_DESKTOP_LINUX_VARIANT === 'vulkan'; let cfg = { asar: true, executableName: 'Goose', - extraResource: ['src/bin', 'src/images'], + extraResource: ['src/bin', 'src/images', 'default-recipes'], icon: 'src/images/icon', // Windows specific configuration win32: { diff --git a/ui/desktop/src/components/settings/extensions/bundled-extensions.json b/ui/desktop/src/components/settings/extensions/bundled-extensions.json index c843abb2c6b4..80120d8241ce 100644 --- a/ui/desktop/src/components/settings/extensions/bundled-extensions.json +++ b/ui/desktop/src/components/settings/extensions/bundled-extensions.json @@ -52,5 +52,19 @@ "type": "builtin", "env_keys": [], "bundled": true + }, + { + "id": "apemind", + "name": "apemind", + "display_name": "ApeMind", + "description": "ApeMind 知识图谱 / RAG 服务(启用前请在扩展设置里替换 URL 与 Authorization Bearer token)", + "enabled": false, + "type": "streamable_http", + "uri": "https://your-apemind.example.com/mcp", + "headers": { + "Authorization": "Bearer your-api-key-here" + }, + "timeout": 300, + "bundled": true } ] diff --git a/ui/desktop/src/components/settings/extensions/bundled-extensions.ts b/ui/desktop/src/components/settings/extensions/bundled-extensions.ts index 332d1dfbad8e..6feb2b4c90a8 100644 --- a/ui/desktop/src/components/settings/extensions/bundled-extensions.ts +++ b/ui/desktop/src/components/settings/extensions/bundled-extensions.ts @@ -17,6 +17,7 @@ type BundledExtension = { uri?: string; envs?: { [key: string]: string }; env_keys?: Array; + headers?: { [key: string]: string }; timeout?: number; allow_configure?: boolean; }; @@ -116,6 +117,9 @@ export async function syncBundledExtensions( description: bundledExt.description, timeout: bundledExt.timeout, uri: bundledExt.uri || '', + envs: bundledExt.envs, + env_keys: bundledExt.env_keys || [], + headers: bundledExt.headers, bundled: true, }; } diff --git a/ui/desktop/src/main.ts b/ui/desktop/src/main.ts index c5fd9b0cf002..87c03b37f20d 100644 --- a/ui/desktop/src/main.ts +++ b/ui/desktop/src/main.ts @@ -175,6 +175,25 @@ function translateMenuLabels(items: MenuItem[]): void { const SETTINGS_FILE = path.join(app.getPath('userData'), 'settings.json'); const STARTUP_LOGS_DIR = path.join(app.getPath('userData'), 'logs', 'startup'); +async function seedDefaultRecipes(): Promise { + const sourceRoot = app.isPackaged + ? path.join(process.resourcesPath, 'default-recipes') + : path.join(__dirname, '..', 'default-recipes'); + const destDir = path.join(os.homedir(), '.config', 'goose', 'recipes'); + + if (!fsSync.existsSync(sourceRoot)) return; + + await fs.mkdir(destDir, { recursive: true }); + const entries = await fs.readdir(sourceRoot); + for (const entry of entries) { + if (!entry.endsWith('.yaml') && !entry.endsWith('.yml')) continue; + const destPath = path.join(destDir, entry); + if (fsSync.existsSync(destPath)) continue; + await fs.copyFile(path.join(sourceRoot, entry), destPath); + log.info(`[seedDefaultRecipes] seeded ${entry} → ${destPath}`); + } +} + function getSettings(): Settings { if (fsSync.existsSync(SETTINGS_FILE)) { let stored: Partial; @@ -2079,6 +2098,12 @@ const registerGlobalShortcuts = () => { async function appMain() { await configureProxy(); + try { + await seedDefaultRecipes(); + } catch (err) { + log.warn('[seedDefaultRecipes] failed:', err); + } + // Ensure Windows shims are available before any MCP processes are spawned await ensureWinShims();