From 1dfc1707162b7a8d799315a4b66943d07683f9fa Mon Sep 17 00:00:00 2001 From: chu Date: Fri, 22 May 2026 22:54:21 +0800 Subject: [PATCH] fix: use atomic write for tui.toml config to prevent corruption saveTuiConfig was writing directly to the target file. If the process crashed or lost power mid-write, tui.toml would be truncated and the user's theme, editor, and notification preferences silently lost on next startup. Now uses the same write-to-temp-then-rename pattern as writeJsonFile in persistence.ts. Co-Authored-By: Claude Opus 4.7 --- apps/kimi-code/src/tui/config.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/apps/kimi-code/src/tui/config.ts b/apps/kimi-code/src/tui/config.ts index e09774a40e..4eff8ad201 100644 --- a/apps/kimi-code/src/tui/config.ts +++ b/apps/kimi-code/src/tui/config.ts @@ -6,8 +6,8 @@ */ import { existsSync } from 'node:fs'; -import { mkdir, readFile, writeFile } from 'node:fs/promises'; -import { dirname, join } from 'node:path'; +import { mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises'; +import { basename, dirname, join } from 'node:path'; import { parse as parseToml } from 'smol-toml'; import { z } from 'zod'; @@ -108,8 +108,17 @@ export async function saveTuiConfig( config: TuiConfig, filePath: string = getTuiConfigPath(), ): Promise { - await mkdir(dirname(filePath), { recursive: true }); - await writeFile(filePath, renderTuiConfig(config), 'utf-8'); + const dir = dirname(filePath); + await mkdir(dir, { recursive: true }); + const nonce = `${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}`; + const tmpPath = join(dir, `.${basename(filePath)}.${nonce}.tmp`); + try { + await writeFile(tmpPath, renderTuiConfig(config), 'utf-8'); + await rename(tmpPath, filePath); + } catch (error) { + await unlink(tmpPath).catch(() => {}); + throw error; + } } export function normalizeTuiConfig(config: TuiConfigFileShape): TuiConfig {