( filePath: string, env: Readonly<Record<string, string | undefined>> = process.env, )
| 130 | * the strict readers so a broken file is never silently rewritten. |
| 131 | */ |
| 132 | export function loadRuntimeConfigSafe( |
| 133 | filePath: string, |
| 134 | env: Readonly<Record<string, string | undefined>> = process.env, |
| 135 | ): RuntimeConfigLoadResult { |
| 136 | const fileWarnings: string[] = []; |
| 137 | let fileError: KimiError | undefined; |
| 138 | let config = getDefaultConfig(); |
| 139 | |
| 140 | let text: string | undefined; |
| 141 | try { |
| 142 | text = existsSync(filePath) ? readFileSync(filePath, 'utf-8') : undefined; |
| 143 | } catch (error) { |
| 144 | fileError = new KimiError( |
| 145 | ErrorCodes.CONFIG_INVALID, |
| 146 | `Failed to read ${filePath}: ${describeUnknownError(error)}`, |
| 147 | { cause: error }, |
| 148 | ); |
| 149 | fileWarnings.push(`Failed to read ${filePath}: ${describeUnknownError(error)}.`); |
| 150 | } |
| 151 | |
| 152 | if (text !== undefined && text.trim().length > 0) { |
| 153 | let data: Record<string, unknown> | undefined; |
| 154 | try { |
| 155 | data = parseToml(text) as Record<string, unknown>; |
| 156 | } catch (error) { |
| 157 | // Same message as the strict parser, code frame included, so failing |
| 158 | // startup points straight at the offending line. |
| 159 | fileError = new KimiError( |
| 160 | ErrorCodes.CONFIG_INVALID, |
| 161 | `Invalid TOML in ${filePath}: ${describeUnknownError(error)}`, |
| 162 | { cause: error }, |
| 163 | ); |
| 164 | fileWarnings.push(`Invalid TOML in ${filePath}: ${describeTomlSyntaxError(error)}.`); |
| 165 | } |
| 166 | if (data !== undefined) { |
| 167 | const raw = cloneRecord(data); |
| 168 | const transformed = transformTomlData(data); |
| 169 | transformed['raw'] = raw; |
| 170 | const salvaged = salvageConfigData(transformed); |
| 171 | if (salvaged.config === undefined) { |
| 172 | fileError = new KimiError( |
| 173 | ErrorCodes.CONFIG_INVALID, |
| 174 | `Invalid configuration in ${filePath}: ${formatConfigValidationError(salvaged.error)}`, |
| 175 | { cause: salvaged.error }, |
| 176 | ); |
| 177 | fileWarnings.push( |
| 178 | `Invalid configuration in ${filePath}: ${formatConfigValidationError(salvaged.error)}.`, |
| 179 | ); |
| 180 | } else { |
| 181 | config = salvaged.config; |
| 182 | if (salvaged.dropped.length > 0) { |
| 183 | fileWarnings.push( |
| 184 | `Ignored invalid config in ${filePath}: ${salvaged.dropped.join(', ')}. Run \`kimi doctor\` for details.`, |
| 185 | ); |
| 186 | } |
| 187 | } |
| 188 | } |
| 189 | } |
no test coverage detected