mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-10-26 02:04:37 +08:00
feat(config): create @openchatlab/config package
Phase 1.6: configuration management with TOML/JSON file support, CHATLAB_* environment variable overrides, and Zod schema validation. Config file location: ~/.chatlab/config.toml
This commit is contained in:
1 parent
6d1408ec56
commit
e49f2fbbe8
6 files changed
+214
-1
No files matched your search
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "@openchatlab/config",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"description": "ChatLab 配置读取层:TOML/JSON 文件 + 环境变量 + Zod 校验",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"dependencies": {
|
||||
"smol-toml": "^1.3.1",
|
||||
"zod": "^3.24.4"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* @openchatlab/config
|
||||
*
|
||||
* ChatLab 配置管理:TOML/JSON 文件读取 + CHATLAB_* 环境变量覆盖 + Zod 校验。
|
||||
*/
|
||||
|
||||
export { loadConfig, getConfigPath, getConfigDir } from './loader'
|
||||
export { configSchema } from './schema'
|
||||
export type { ChatLabConfig, LlmConfig, DataConfig, ApiConfig, LocaleConfig } from './schema'
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* 配置加载器
|
||||
*
|
||||
* 优先级(从高到低):
|
||||
* 1. CHATLAB_* 环境变量
|
||||
* 2. ~/.chatlab/config.toml(或 config.json)
|
||||
* 3. Zod schema 默认值
|
||||
*/
|
||||
|
||||
import * as fs from 'fs'
|
||||
import * as os from 'os'
|
||||
import * as path from 'path'
|
||||
import { parse as parseToml } from 'smol-toml'
|
||||
import { configSchema, type ChatLabConfig } from './schema'
|
||||
|
||||
const CONFIG_DIR = path.join(os.homedir(), '.chatlab')
|
||||
const CONFIG_TOML = path.join(CONFIG_DIR, 'config.toml')
|
||||
const CONFIG_JSON = path.join(CONFIG_DIR, 'config.json')
|
||||
|
||||
/**
|
||||
* 加载完整配置
|
||||
*/
|
||||
export function loadConfig(overrides?: Partial<ChatLabConfig>): ChatLabConfig {
|
||||
const fileConfig = loadConfigFile()
|
||||
const envConfig = loadEnvConfig()
|
||||
|
||||
const merged = deepMerge(deepMerge(fileConfig, envConfig), overrides ?? {})
|
||||
return configSchema.parse(merged)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置文件路径(返回实际存在的路径或默认 TOML 路径)
|
||||
*/
|
||||
export function getConfigPath(): string {
|
||||
if (fs.existsSync(CONFIG_TOML)) return CONFIG_TOML
|
||||
if (fs.existsSync(CONFIG_JSON)) return CONFIG_JSON
|
||||
return CONFIG_TOML
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置目录路径
|
||||
*/
|
||||
export function getConfigDir(): string {
|
||||
return CONFIG_DIR
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件加载配置
|
||||
*/
|
||||
function loadConfigFile(): Record<string, unknown> {
|
||||
if (fs.existsSync(CONFIG_TOML)) {
|
||||
try {
|
||||
const content = fs.readFileSync(CONFIG_TOML, 'utf-8')
|
||||
return parseToml(content) as Record<string, unknown>
|
||||
} catch (err) {
|
||||
console.warn(`[Config] Failed to parse ${CONFIG_TOML}:`, err)
|
||||
}
|
||||
}
|
||||
|
||||
if (fs.existsSync(CONFIG_JSON)) {
|
||||
try {
|
||||
const content = fs.readFileSync(CONFIG_JSON, 'utf-8')
|
||||
return JSON.parse(content) as Record<string, unknown>
|
||||
} catch (err) {
|
||||
console.warn(`[Config] Failed to parse ${CONFIG_JSON}:`, err)
|
||||
}
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 CHATLAB_* 环境变量加载配置
|
||||
*
|
||||
* 映射规则:
|
||||
* - CHATLAB_DATA_DIR -> data.dir
|
||||
* - CHATLAB_API_PORT -> api.port
|
||||
* - CHATLAB_API_HOST -> api.host
|
||||
* - CHATLAB_LLM_PROVIDER -> llm.provider
|
||||
* - CHATLAB_LLM_API_KEY -> llm.api_key
|
||||
* - CHATLAB_LLM_MODEL -> llm.model
|
||||
* - CHATLAB_LLM_BASE_URL -> llm.base_url
|
||||
* - CHATLAB_LOCALE_LANG -> locale.lang
|
||||
*/
|
||||
function loadEnvConfig(): Record<string, unknown> {
|
||||
const result: Record<string, Record<string, unknown>> = {}
|
||||
|
||||
const envMap: Array<{ env: string; section: string; key: string; transform?: (v: string) => unknown }> = [
|
||||
{ env: 'CHATLAB_DATA_DIR', section: 'data', key: 'dir' },
|
||||
{ env: 'CHATLAB_API_PORT', section: 'api', key: 'port', transform: (v) => parseInt(v, 10) },
|
||||
{ env: 'CHATLAB_API_HOST', section: 'api', key: 'host' },
|
||||
{ env: 'CHATLAB_LLM_PROVIDER', section: 'llm', key: 'provider' },
|
||||
{ env: 'CHATLAB_LLM_API_KEY', section: 'llm', key: 'api_key' },
|
||||
{ env: 'CHATLAB_LLM_MODEL', section: 'llm', key: 'model' },
|
||||
{ env: 'CHATLAB_LLM_BASE_URL', section: 'llm', key: 'base_url' },
|
||||
{ env: 'CHATLAB_LOCALE_LANG', section: 'locale', key: 'lang' },
|
||||
]
|
||||
|
||||
for (const { env, section, key, transform } of envMap) {
|
||||
const value = process.env[env]
|
||||
if (value !== undefined && value !== '') {
|
||||
if (!result[section]) result[section] = {}
|
||||
result[section][key] = transform ? transform(value) : value
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单的深度合并(仅处理纯对象,不处理数组)
|
||||
*/
|
||||
function deepMerge(base: Record<string, unknown>, override: Record<string, unknown>): Record<string, unknown> {
|
||||
const result = { ...base }
|
||||
for (const [key, value] of Object.entries(override)) {
|
||||
if (value !== undefined && value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
||||
result[key] = deepMerge(
|
||||
(result[key] as Record<string, unknown>) ?? {},
|
||||
value as Record<string, unknown>
|
||||
)
|
||||
} else if (value !== undefined) {
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 配置 Zod Schema 定义
|
||||
*
|
||||
* 同时服务于 TOML 文件校验和环境变量解析。
|
||||
*/
|
||||
|
||||
import { z } from 'zod'
|
||||
|
||||
export const llmConfigSchema = z.object({
|
||||
provider: z.string().default(''),
|
||||
api_key: z.string().default(''),
|
||||
model: z.string().default(''),
|
||||
base_url: z.string().default(''),
|
||||
})
|
||||
|
||||
export const dataConfigSchema = z.object({
|
||||
dir: z.string().default(''),
|
||||
})
|
||||
|
||||
export const apiConfigSchema = z.object({
|
||||
port: z.number().int().min(1).max(65535).default(3210),
|
||||
host: z.string().default('127.0.0.1'),
|
||||
})
|
||||
|
||||
export const localeConfigSchema = z.object({
|
||||
lang: z.string().default('zh-CN'),
|
||||
})
|
||||
|
||||
export const configSchema = z.object({
|
||||
llm: llmConfigSchema.default({}),
|
||||
data: dataConfigSchema.default({}),
|
||||
api: apiConfigSchema.default({}),
|
||||
locale: localeConfigSchema.default({}),
|
||||
})
|
||||
|
||||
export type ChatLabConfig = z.infer<typeof configSchema>
|
||||
export type LlmConfig = z.infer<typeof llmConfigSchema>
|
||||
export type DataConfig = z.infer<typeof dataConfigSchema>
|
||||
export type ApiConfig = z.infer<typeof apiConfigSchema>
|
||||
export type LocaleConfig = z.infer<typeof localeConfigSchema>
|
||||
Generated
+25
@@ -142,6 +142,25 @@ importers:
|
||||
specifier: ^3.1.1
|
||||
version: 3.2.6(typescript@5.9.3)
|
||||
|
||||
packages/config:
|
||||
dependencies:
|
||||
smol-toml:
|
||||
specifier: ^1.3.1
|
||||
version: 1.6.1
|
||||
zod:
|
||||
specifier: ^3.24.4
|
||||
version: 3.25.76
|
||||
|
||||
packages/core: {}
|
||||
|
||||
packages/node-runtime:
|
||||
dependencies:
|
||||
'@openchatlab/core':
|
||||
specifier: workspace:*
|
||||
version: link:../core
|
||||
|
||||
packages/shared-types: {}
|
||||
|
||||
packages:
|
||||
|
||||
7zip-bin@5.2.0:
|
||||
@@ -4311,6 +4330,10 @@ packages:
|
||||
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
|
||||
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
|
||||
|
||||
smol-toml@1.6.1:
|
||||
resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
socks-proxy-agent@8.0.5:
|
||||
resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==}
|
||||
engines: {node: '>= 14'}
|
||||
@@ -10016,6 +10039,8 @@ snapshots:
|
||||
|
||||
smart-buffer@4.2.0: {}
|
||||
|
||||
smol-toml@1.6.1: {}
|
||||
|
||||
socks-proxy-agent@8.0.5:
|
||||
dependencies:
|
||||
agent-base: 7.1.4
|
||||
|
||||
+2
-1
@@ -9,7 +9,8 @@
|
||||
"src/types/**/*",
|
||||
"packages/shared-types/**/*",
|
||||
"packages/core/**/*",
|
||||
"packages/node-runtime/**/*"
|
||||
"packages/node-runtime/**/*",
|
||||
"packages/config/**/*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
|
||||
Reference in new issue
Block a user