
Cherry Studio Boot Config 詳解schema 自動生成、運行時校驗與 V1→V2 遷移管線【免費下載鏈接】cherry-studioAI productivity studio with smart chat, autonomous agents, and 300 assistants. Unified access to frontier LLMs項目地址: https://gitcode.com/GitHub_Trending/ch/cherry-studio本文以 Cherry Studio 的docs/references/data/boot-config-schema-guide.md為骨架講清兩件事如何向自動生成的 BootConfig schema 中正確添加啟動配置鍵命名規范、生成器、BootConfigService運行時行為、統一偏好 API 接入以及scripts/data-classify工具鏈如何把 V1 時代的遺留數據Redux / ElectronStore / Dexie / localStorage / 舊版 home 配置文件遷移到 V2 啟動配置系統。讀完后你能獨立新增一個 BootConfig 鍵并跑通從分類定義、代碼生成到遷移映射的全鏈路同時理解該機制在啟動時序上的設計邊界。一、BootConfig 的適用范圍一張決策表Cherry Studio 的啟動期配置分屬兩套系統BootConfig主進程最早期加載的極少量配置與Preference常規偏好設置。文檔給出的核心原則是BootConfig 只服務于一個非常窄的配置集合新增鍵之前必須先過這張決策表問題若答案為“是”若答案為“否”必須在生命周期系統接管之前加載嗎BootConfigPreference是否影響進程級行為Chromium flags、數據目錄BootConfigPreference可以等到BeforeReady生命周期階段再讀嗎PreferenceBootConfig可以在運行時修改而無需重啟嗎PreferenceBootConfig文檔給出的經驗法則是如果一個設置能等到生命周期的BeforeReady階段它就屬于 PreferenceBootConfig 只保留必須在生命周期系統啟動前就可用的設置并保持最小化。從源碼結構看這個“最早期”是有嚴格時序約束的。主進程入口 中import main/data/bootConfig是全文件第一條 import并配有注釋 “BootConfig must load before any other import (configures userData path)”——因為用戶數據目錄的位置本身就由 BootConfig 決定。緊隨其后的 preboot 調用順序是resolveUserDataLocation() → requireSingleInstance() → configureChromiumFlags() → initCrashTelemetry()之后才進入備份恢復門、V2 遷移門最終application.bootstrap()啟動生命周期。這也印證了“BootConfig 決定 userData 在哪里而不是相反”的注釋見 BootConfigService 構造函數。二、鍵命名規范與 Preference 完全一致BootConfig 鍵沿用與 preferences 相同的命名約定格式namespace.key_name至少 2 段以點分隔字符集僅小寫字母、數字、下劃線正則模式/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)$/語義約定點表示層級下劃線表示多詞名。文檔中的有效性對照表合法非法原因app.disable_hardware_accelerationdisableHardwareAcceleration缺少點分隔app.user_data_pathApp.userDataPath大寫、駝峰chromium.gpu_compositinggpu單段注意temp.*前綴是一個特例命名空間它保留給主進程內部的瞬時運行時狀態刻意排除在統一偏好 API 之外不在UnifiedPreferenceType中、無法通過usePreference訪問、并在 PreferenceService 的 IPC 邊界被拒絕。這一排除在類型層面是靜態強制的bootConfigTypes.ts 中InternalBootConfigKey ExtractBootConfigKey, \temp.${string}PublicBootConfigKey再將其排除最終BootConfigPreferenceKeys這個映射類型只會為 public 鍵自動生成BootConfig.前綴。訪問temp.*鍵的唯一途徑是直接調用bootConfigService并通過onChange() 訂閱變化。三、新增一個 BootConfig 鍵的四步流程Step 1進入生成器輸入禁止手改 schema 文件目標文件 src/shared/data/bootConfig/bootConfigSchemas.ts 是完全自動生成的——文件頭明確標注 “Auto-generated … DO NOT edit by hand”并給出重新生成的命令node scripts/data-classify/scripts/generate-boot-config.js。zod schema 是單一事實來源BootConfigSchema類型由它推導BootConfigService在運行時對文件加載值和set()值做校驗。文檔給出的最小形態示例export const bootConfigSchema z.object({ app.disable_hardware_acceleration: z.boolean(), app.user_data_path: z.record(z.string(), z.string()) }) export type BootConfigSchema z.infertypeof bootConfigSchema export const DefaultBootConfig: BootConfigSchema { app.disable_hardware_acceleration: false, app.user_data_path: {} }實際生成文件中還包含第三個鍵temp.user_data_relocation一個pending/failed兩種狀態對象聯合的 nullable 類型用于跨啟動傳遞 Electron userData 目錄遷移任務它正體現了第二節所述的temp.*內部命名空間用法。修改入口分兩類文檔明確要求不能直接改生成物從受支持的 V1 來源遷移的鍵編輯 scripts/data-classify/data/classification.json無遺留來源的新鍵、或來自配置文件的鍵加入 generate-boot-config.js 頂部的MANUAL_BOOT_CONFIG_ITEMS復雜類型必須顯式給出zodType表達式字符串。關于生成器的幾個源碼級細節值得注意生成器只接受四類分類來源electronStore、redux、localStorage、dexieSettings當同一個targetKey在多個來源出現時按redux(4) dexieSettings(3) localStorage(2) electronStore(1)的優先級去重并打印警告見extractBootConfigData()mapZodType()刻意不做typeof defaultValue之類的隱式回退——無法映射的類型會直接拋錯中止生成防止錯誤的 schema 被靜默寫入defaultValue支持VALUE: xxx轉義前綴用于輸出原始 JS 字面量例如{}避免被當成字符串{}輸出按targetKey字典序排序每個鍵上方帶一行// source/category/originalKey溯源注釋保證生成結果可 diff、可追溯。Step 2按需添加自定義類型文件src/shared/data/bootConfig/bootConfigTypes.ts簡單類型boolean、string、number無需任何改動——類型直接從 schema 推導。需要聯合字面量等復雜語義時與BootConfigKey并列定義即可export type BootConfigKey keyof BootConfigSchema // Custom types if needed export type GpuMode auto | disabled | software實際文件還包含InternalBootConfigKey、PublicBootConfigKey與BootConfigPreferenceKeys三個派生類型構成temp.*鍵隔離的靜態機制見第二節。Step 3在早期啟動代碼中使用如需要僅針對必須在生命周期之前生效的設置。文件src/main/main.ts。文檔給出的范式import { bootConfigService } from main/data/bootConfig // Apply before app.whenReady() if (bootConfigService.get(app.disable_hardware_acceleration)) { app.disableHardwareAcceleration() }當前倉庫中這一機制的對應實現位于 preboot 階段configureChromiumFlags()src/main/core/preboot/chromiumFlags.ts在模塊求值階段讀取 BootConfig 并設置 Chromium flags而resolveUserDataLocation()則消費app.user_data_path決定 Electron 的 userData 目錄。入口文件自身的注釋也明確告誡“DO NOT add new code here”新服務應放入生命周期系統、不可移除的 preboot 步驟放入core/preboot/。Step 4從渲染進程 / 生命周期服務訪問零接線無需額外 wiringBootConfigPreferenceKeys映射類型自動為每個 public 鍵添加BootConfig.前綴使其立即可通過統一偏好 API 使用// Renderer — 加入 schema 后立即可用 const [disableHardwareAcceleration, setDisableHardwareAcceleration] usePreference( BootConfig.app.disable_hardware_acceleration ) // Main process lifecycle service const disableHardwareAcceleration preferenceService.get(BootConfig.app.disable_hardware_acceleration)temp.*鍵是例外temp.前綴下的鍵是主進程內部瞬時狀態刻意不出現在UnifiedPreferenceType中、無法經usePreference觸達、并在 PreferenceService 的 IPC 邊界被拒絕。只能通過bootConfigService直接訪問任何階段均可變更通知走bootConfigService.onChange()。usePreference的完整用法參見 Preference Usage Guide。四、BootConfigService運行時行為與文件布局文檔“File Structure”一節的文件職責表結合當前倉庫實際情況文件用途src/shared/data/bootConfig/bootConfigSchemas.tsZod value schema單一事實來源、推導的BootConfigSchema類型、默認值src/shared/data/bootConfig/bootConfigTypes.tsBootConfigKey、Public/InternalBootConfigKey、BootConfigPreferenceKeys映射類型src/main/data/bootConfig/BootConfigService.ts服務實現同步加載、防抖保存、校驗、訂閱src/main/data/bootConfig/types.tsBootConfigLoadError類型scripts/data-classify/data/classification.json遷移事實來源scripts/data-classify/scripts/generate-boot-config.jsSchema 生成器遷移管線src/main/data/migration/v2/migrators/BootConfigMigrator.ts遷移執行器src/main/data/migration/v2/migrators/mappings/BootConfigMappings.ts自動生成的遷移映射從 BootConfigService.ts 源碼可以進一步確認文檔所述行為的實現細節這些細節對理解“為什么這么設計”很有價值存儲位置配置文件是~/.cherrystudio/boot-config.json常量BOOT_CONFIG_PATH定義于 src/main/core/paths/constants.ts。刻意放在~/.cherrystudio/而非 userData 下原因有二它要能決定 userData 去哪里不能反過來被appDataPath影響且它必須在initAppDataDir()改寫 userData 路徑之前就可讀。constants.ts還被特意做成零業務依賴模塊避免該服務引入重 import。加載構造函數中同步加載模塊 import 時即完成文件不存在時用DefaultBootConfig這解釋了最佳實踐第 2 條——缺默認值的鍵在首啟不可用JSON 解析失敗記parse_error逐鍵 schema 校驗失敗記validation_error并把壞鍵回退默認值讀失敗記read_error三類錯誤結構見 types.ts。寫入set()先經bootConfigSchema.shape[key].safeParse校驗校驗失敗拋異常且不做任何狀態變更——這是 Preference IPC 路由與 V1 遷移器兩條不可信數據路徑上的唯一強制點校驗通過后才更新內存、置 dirty 并觸發 350ms 防抖保存。落盤策略只寫與默認值不同的鍵diff 寫入若所有值都是默認值則直接刪除文件讓“全默認狀態不留盤”實際寫入采用臨時文件writeFileSyncrenameSync的原子模式。持久化語義分層persist()嚴格寫盤且傳播失敗遷移器、IPC handler 用flush()是 best-effort 包裝關機、preboot 路徑用失敗只記日志防抖自動保存也是 best-effort——三者共享 dirty 標志以便失敗后重試。五、V1 到 V2 的數據遷移管線本節覆蓋的是把 V1Redux / ElectronStore / Dexie遺留數據遷入 V2 BootConfig 的遷移工具鏈不是日常新增鍵的常規路徑。5.1 管線總覽scripts/data-classify/目錄承載代碼生成管線classification.json 是唯一事實來源把每個遺留鍵分類到目標系統Preference、BootConfig、Cache 或 DataApi。工作流程classification.json中每個category: bootConfig的條目把一個遺留鍵映射到一個 boot config 鍵生成器讀取這些分類產出兩樣東西src/shared/data/bootConfig/bootConfigSchemas.ts——zod schema、推導出的BootConfigSchema類型與默認值src/main/data/migration/v2/migrators/mappings/BootConfigMappings.ts——舊鍵到新鍵的映射表遷移時刻BootConfigMigrator從各遺留來源讀值并寫入bootConfigService。5.2 遷移來源來源訪問器示例Redux StoreReduxStateReadercategory 點路徑settings.disableHardwareAccelerationElectronStoreElectronStoreReader.get(key)直接按鍵查找Dexie settings鍵值表直接按鍵查找localStoragelocalStorage.getItem(key)直接按鍵查找舊版 home 配置文件LegacyHomeConfigReader~/.cherrystudio/config/config.json僅appDataPath字段5.3 配置文件來源的映射是手工維護的文檔特別強調data-classify工具鏈的classification.json尚不建模配置文件來源因此在兩處由一份小型手工清單補充分類驅動的管線Schema 鍵generate-boot-config.js 頂部的MANUAL_BOOT_CONFIG_ITEMS——這些條目與分類推導條目合并后走同一套排序/輸出代碼最終輸出仍是完全自動生成的單文件無手工區段。每個手工條目需要顯式zodType表達式字符串分類推導的簡單類型會自動映射到 zod生成器遇到無法映射的條目會中止。當前倉庫中該列表包含兩條app.user_data_path來源configfile/legacy-home/appDataPath與temp.user_data_relocation來源preboot/transient/userDataRelocation。映射BootConfigMigrator.loadMigrationItems()內聯的configFileMappings——一個ReadonlyArray{ originalKey: string; targetKey: BootConfigKey }其BootConfigKey類型標注就是重新生成的安全網如果 schema 中丟掉app.user_data_path這個數組字面量會在聲明處編譯失敗見 BootConfigMigrator.ts 附近注釋。新增一個配置文件來源的鍵的完整步驟往MANUAL_BOOT_CONFIG_ITEMS加條目 → 往BootConfigMigrator.loadMigrationItems()的configFileMappings加對應條目 → 運行npm run generate。5.4 添加一條遷移映射把遺留鍵遷到 boot config在 classification.json 中添加或更新條目{ originalKey: disableHardwareAcceleration, source: redux, category: bootConfig, status: classified, targetKey: app.disable_hardware_acceleration, targetType: boolean, defaultValue: false, reduxCategory: settings }重新生成映射cd scripts/data-classify npm run generate檢查BootConfigMappings.ts中的生成結果。5.5 當前映射表遺留來源遺留鍵目標鍵ReduxsettingsdisableHardwareAccelerationapp.disable_hardware_acceleration配置文件~/.cherrystudio/config/config.jsonappDataPathapp.user_data_pathAppImage / Windows 便攜版可執行文件路徑的特判V1 的~/.cherrystudio/config/config.json把appDataPath存成以可執行路徑為鍵的{ executablePath, dataPath }數組。AppImageLinux與 Windows 便攜版構建使用的規范化可執行鍵與app.getPath(exe)不同因為這兩類構建的原始 exe 路徑在不同啟動之間不穩定AppImagepath.dirname(process.env.APPIMAGE) /cherry-studio.appimageWindows 便攜版process.env.PORTABLE_EXECUTABLE_DIR /cherry-studio-portable.exeresolveMigrationPaths()與運行時用戶數據位置解析器都使用 src/main/core/preboot/userDataLocation.ts 中的getNormalizedExecutablePath()保證遷移時的寫入鍵和運行時的查找鍵嚴格一致。這正是 bootConfigSchemas.ts 中app.user_data_pathJSDoc 所描述的“按可執行路徑鍵控的 Record、同機多安裝stable / dev / portable各自獨立數據目錄”設計的由來。另一個從遷移器文檔可補充的細節配置文件來源的條目把defaultValue設為null是有意為之——其他來源在源無值時會回退DefaultBootConfig[targetKey]但對配置文件來源“v1 文件不存在”應表示“無東西可遷移”若寫入 schema 默認值{}會制造一次虛假遷移。null默認值讓該條目走共享的 null-skip 守衛被整體跳過見 BootConfigMigrator 說明文檔。六、最佳實踐文檔原文四條逐條落地保持 BootConfig 最小化——絕大多數設置屬于 Preference。BootConfig 只給必須在生命周期系統接管前加載的設置使用提供合理的默認值——BootConfigService首啟文件缺失時直接使用默認值缺默認值意味著該鍵在首啟不可用實現上即 loadSync() 中return { ...DefaultBootConfig }分支遵循命名規范——與 preferences 使用同一套namespace.key_name模式保持一致性進程級設置需要重啟——在把 boot config 設置暴露給用戶時于 UI 中說明這一點。七、延伸閱讀Boot Config Overview——架構與加載時序含 Internaltemp.*namespace 專節Preference Schema Guide——新增非 boot 的 preference 鍵Preference Usage Guide——usePreferencehook 與服務端 APIV2 Migration Guide——完整遷移系統文檔【免費下載鏈接】cherry-studioAI productivity studio with smart chat, autonomous agents, and 300 assistants. Unified access to frontier LLMs項目地址: https://gitcode.com/GitHub_Trending/ch/cherry-studio創作聲明:本文部分內容由AI輔助生成(AIGC),僅供參考