From 47ba99dd982d5cd26fdedbdb27f79c70c601fa1d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 9 May 2026 05:10:56 +0100 Subject: [PATCH] refactor: keep legacy state paths in doctor --- docs/refactor/database-first.md | 7 +- src/acp/event-ledger.ts | 10 ++- src/commands/doctor-cron.ts | 8 +-- .../doctor/legacy/acp-event-ledger.ts | 8 +-- src/commands/doctor/legacy/cron-store.ts | 50 ++++++++++---- .../doctor/legacy/device-bootstrap.ts | 5 +- src/commands/doctor/legacy/pairing-files.ts | 22 +++++-- src/commands/doctor/legacy/tts-prefs.ts | 14 ++-- src/cron/store.test.ts | 9 +-- src/cron/store.ts | 65 +++++-------------- src/infra/device-pairing.test.ts | 19 ++++-- src/infra/node-pairing.test.ts | 16 ++++- src/infra/pairing-files.ts | 12 ---- src/tts/status-config.test.ts | 8 +-- src/tts/tts-config.test.ts | 8 +-- src/tts/tts-prefs-store.ts | 8 +-- 16 files changed, 132 insertions(+), 137 deletions(-) diff --git a/docs/refactor/database-first.md b/docs/refactor/database-first.md index ea057e3eb1a..986c1a0ffbf 100644 --- a/docs/refactor/database-first.md +++ b/docs/refactor/database-first.md @@ -131,7 +131,8 @@ The branch already has a real shared SQLite base: The old TUI JSON file is doctor migration input only. - Default TTS prefs now live in shared plugin-state SQLite rows keyed under the `speech-core` plugin. The old `settings/tts.json` file is doctor migration - input only; runtime no longer reads or writes TTS prefs JSON files. + input only; runtime no longer reads or writes TTS prefs JSON files, and the + legacy path resolver lives in the doctor migration module. - Subagent run recovery and OpenRouter model capability cache runtime modules now keep SQLite readers/writers separate from doctor-only legacy JSON import helpers. @@ -154,6 +155,10 @@ The branch already has a real shared SQLite base: same split: runtime modules expose SQLite-backed operations and narrow migration writers, while doctor imports/removes the old JSON files through `src/commands/doctor/legacy/*` modules. +- Core pairing and cron runtime modules no longer export legacy JSON path + builders. Doctor-owned legacy modules construct `pending.json`, `paired.json`, + `bootstrap.json`, and `cron/jobs.json` source paths for import tests and + migration only. - `src/commands/doctor-sqlite-state.ts` already imports several legacy JSON state files, including node host config, into SQLite from doctor. - `src/commands/doctor/state-migrations.ts` imports legacy `sessions.json` and diff --git a/src/acp/event-ledger.ts b/src/acp/event-ledger.ts index 8d80c86fd0e..8ac3ee5c921 100644 --- a/src/acp/event-ledger.ts +++ b/src/acp/event-ledger.ts @@ -72,7 +72,7 @@ type LedgerStore = { sessions: Record; }; -export type AcpEventLedgerMigrationStore = LedgerStore; +export type AcpEventLedgerSnapshot = LedgerStore; type LedgerOptions = { maxSessions?: number; @@ -622,14 +622,12 @@ function writeStoreToSqlite( }, options); } -export function normalizeAcpEventLedgerStoreForMigration( - raw: unknown, -): AcpEventLedgerMigrationStore { +export function normalizeAcpEventLedgerSnapshot(raw: unknown): AcpEventLedgerSnapshot { return normalizeStore(raw); } -export function writeAcpEventLedgerStoreToSqliteForMigration( - store: AcpEventLedgerMigrationStore, +export function writeAcpEventLedgerSnapshotToSqlite( + store: AcpEventLedgerSnapshot, options: OpenClawStateDatabaseOptions & { now?: () => number } = {}, ): void { writeStoreToSqlite(store, { diff --git a/src/commands/doctor-cron.ts b/src/commands/doctor-cron.ts index 84071ab3eb3..fc875d93f9a 100644 --- a/src/commands/doctor-cron.ts +++ b/src/commands/doctor-cron.ts @@ -4,12 +4,7 @@ import { promisify } from "node:util"; import { formatCliCommand } from "../cli/command-format.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { resolveCronRunLogPruneOptions } from "../cron/run-log.js"; -import { - resolveCronStoreKey, - resolveLegacyCronStorePath, - loadCronStore, - saveCronStore, -} from "../cron/store.js"; +import { resolveCronStoreKey, loadCronStore, saveCronStore } from "../cron/store.js"; import type { CronJob } from "../cron/types.js"; import { normalizeOptionalLowercaseString, @@ -32,6 +27,7 @@ import { legacyCronStoreFileExists, legacyCronStateFileExists, loadLegacyCronStoreForMigration, + resolveLegacyCronStorePath, } from "./doctor/legacy/cron-store.js"; type CronDoctorOutcome = { diff --git a/src/commands/doctor/legacy/acp-event-ledger.ts b/src/commands/doctor/legacy/acp-event-ledger.ts index 7cd62310946..9cfb37f32d1 100644 --- a/src/commands/doctor/legacy/acp-event-ledger.ts +++ b/src/commands/doctor/legacy/acp-event-ledger.ts @@ -3,8 +3,8 @@ import fs from "node:fs/promises"; import path from "node:path"; import { ACP_EVENT_LEDGER_VERSION, - normalizeAcpEventLedgerStoreForMigration, - writeAcpEventLedgerStoreToSqliteForMigration, + normalizeAcpEventLedgerSnapshot, + writeAcpEventLedgerSnapshotToSqlite, } from "../../../acp/event-ledger.js"; import { resolveStateDir } from "../../../config/paths.js"; @@ -54,8 +54,8 @@ export async function importLegacyAcpEventLedgerFileToSqlite( if (!isLegacyAcpEventLedgerShape(parsed)) { return { imported: false, sessions: 0, events: 0 }; } - const store = normalizeAcpEventLedgerStoreForMigration(parsed); - writeAcpEventLedgerStoreToSqliteForMigration(store, { env }); + const store = normalizeAcpEventLedgerSnapshot(parsed); + writeAcpEventLedgerSnapshotToSqlite(store, { env }); await fs.rm(filePath, { force: true }).catch(() => undefined); return { imported: true, diff --git a/src/commands/doctor/legacy/cron-store.ts b/src/commands/doctor/legacy/cron-store.ts index 0785a1b541a..e25ecbf4c4c 100644 --- a/src/commands/doctor/legacy/cron-store.ts +++ b/src/commands/doctor/legacy/cron-store.ts @@ -1,14 +1,36 @@ import fs from "node:fs"; +import path from "node:path"; import { - extractCronStateFileForMigration, - type CronStateFile, - type CronStateFileEntry, - writeCronJobRuntimeStateForMigration, - writeCronJobsForMigration, + extractCronRuntimeStateSnapshot, + saveCronStore, + type CronRuntimeStateEntry, + type CronRuntimeStateSnapshot, + writeCronRuntimeStateSnapshot, } from "../../../cron/store.js"; import type { CronStoreFile } from "../../../cron/types.js"; +import { expandHomePrefix } from "../../../infra/home-dir.js"; +import { resolveConfigDir } from "../../../utils.js"; import { parseJsonWithJson5Fallback } from "../../../utils/parse-json-compat.js"; +function resolveDefaultCronDir(): string { + return path.join(resolveConfigDir(), "cron"); +} + +function resolveDefaultLegacyCronStorePath(): string { + return path.join(resolveDefaultCronDir(), "jobs.json"); +} + +export function resolveLegacyCronStorePath(configuredLegacyStorePath?: string): string { + if (configuredLegacyStorePath?.trim()) { + const raw = configuredLegacyStorePath.trim(); + if (raw.startsWith("~")) { + return path.resolve(expandHomePrefix(raw)); + } + return path.resolve(raw); + } + return resolveDefaultLegacyCronStorePath(); +} + function resolveStatePath(storePath: string): string { if (storePath.endsWith(".json")) { return storePath.replace(/\.json$/, "-state.json"); @@ -20,16 +42,16 @@ function isRecord(value: unknown): value is Record { return Boolean(value) && typeof value === "object" && !Array.isArray(value); } -function normalizeCronStateFile(value: unknown): CronStateFile | null { +function normalizeCronStateFile(value: unknown): CronRuntimeStateSnapshot | null { if (!isRecord(value) || value.version !== 1 || !isRecord(value.jobs)) { return null; } - const jobs: Record = {}; + const jobs: Record = {}; for (const [jobId, entry] of Object.entries(value.jobs)) { if (!isRecord(entry)) { continue; } - const normalized: CronStateFileEntry = {}; + const normalized: CronRuntimeStateEntry = {}; if (typeof entry.updatedAtMs === "number" && Number.isFinite(entry.updatedAtMs)) { normalized.updatedAtMs = entry.updatedAtMs; } @@ -60,7 +82,7 @@ export function legacyCronStateFileExists(storePath: string): boolean { } } -async function loadStateFile(statePath: string): Promise { +async function loadStateFile(statePath: string): Promise { let raw: string; try { raw = await fs.promises.readFile(statePath, "utf-8"); @@ -125,7 +147,7 @@ export async function importLegacyCronStateFileToSqlite(params: { if (!stateFile) { return { imported: false, importedJobs: 0 }; } - const importedJobs = writeCronJobRuntimeStateForMigration(params.storeKey, stateFile); + const importedJobs = writeCronRuntimeStateSnapshot(params.storeKey, stateFile); try { await fs.promises.rm(statePath, { force: true }); } catch { @@ -150,11 +172,11 @@ export async function importLegacyCronStoreToSqlite(params: { if (!store) { return { imported: false, importedJobs: 0 }; } - const stateFile = + const stateSnapshot = (await loadStateFile(resolveStatePath(params.legacyStorePath))) ?? - extractCronStateFileForMigration(store); - writeCronJobsForMigration(params.storeKey, store); - writeCronJobRuntimeStateForMigration(params.storeKey, stateFile); + extractCronRuntimeStateSnapshot(store); + await saveCronStore(params.storeKey, store); + writeCronRuntimeStateSnapshot(params.storeKey, stateSnapshot); try { await fs.promises.rm(params.legacyStorePath, { force: true }); } catch { diff --git a/src/commands/doctor/legacy/device-bootstrap.ts b/src/commands/doctor/legacy/device-bootstrap.ts index a29edd2ac75..d92c2387457 100644 --- a/src/commands/doctor/legacy/device-bootstrap.ts +++ b/src/commands/doctor/legacy/device-bootstrap.ts @@ -1,10 +1,11 @@ import fs from "node:fs/promises"; import path from "node:path"; import { type DeviceBootstrapState } from "../../../infra/device-bootstrap.js"; -import { resolvePairingPaths, writePairingStateRecord } from "../../../infra/pairing-files.js"; +import { writePairingStateRecord } from "../../../infra/pairing-files.js"; +import { resolveLegacyPairingPaths } from "./pairing-files.js"; function resolveBootstrapPath(baseDir?: string): string { - return path.join(resolvePairingPaths(baseDir, "devices").dir, "bootstrap.json"); + return path.join(resolveLegacyPairingPaths(baseDir, "devices").dir, "bootstrap.json"); } export async function legacyDeviceBootstrapFileExists(baseDir?: string): Promise { diff --git a/src/commands/doctor/legacy/pairing-files.ts b/src/commands/doctor/legacy/pairing-files.ts index c689314bb57..48e7e26f4c6 100644 --- a/src/commands/doctor/legacy/pairing-files.ts +++ b/src/commands/doctor/legacy/pairing-files.ts @@ -1,16 +1,24 @@ import fs from "node:fs/promises"; +import path from "node:path"; +import { resolveStateDir } from "../../../config/paths.js"; import { readJsonIfExists } from "../../../infra/json-files.js"; -import { - coercePairingStateRecord, - resolvePairingPaths, - writePairingStateRecord, -} from "../../../infra/pairing-files.js"; +import { coercePairingStateRecord, writePairingStateRecord } from "../../../infra/pairing-files.js"; + +export function resolveLegacyPairingPaths(baseDir: string | undefined, subdir: string) { + const root = baseDir ?? resolveStateDir(); + const dir = path.join(root, subdir); + return { + dir, + pendingPath: path.join(dir, "pending.json"), + pairedPath: path.join(dir, "paired.json"), + }; +} export async function legacyPairingStateFilesExist(params: { baseDir?: string; subdir: string; }): Promise { - const { pendingPath, pairedPath } = resolvePairingPaths(params.baseDir, params.subdir); + const { pendingPath, pairedPath } = resolveLegacyPairingPaths(params.baseDir, params.subdir); const [pendingExists, pairedExists] = await Promise.all([ fs .access(pendingPath) @@ -32,7 +40,7 @@ export async function importLegacyPairingStateFilesToSqlite(params: { paired: number; files: number; }> { - const { pendingPath, pairedPath } = resolvePairingPaths(params.baseDir, params.subdir); + const { pendingPath, pairedPath } = resolveLegacyPairingPaths(params.baseDir, params.subdir); const [pending, paired] = await Promise.all([ readJsonIfExists(pendingPath), readJsonIfExists(pairedPath), diff --git a/src/commands/doctor/legacy/tts-prefs.ts b/src/commands/doctor/legacy/tts-prefs.ts index c646e07db71..88c7ab11f2f 100644 --- a/src/commands/doctor/legacy/tts-prefs.ts +++ b/src/commands/doctor/legacy/tts-prefs.ts @@ -1,9 +1,11 @@ import fs from "node:fs/promises"; -import { - resolveLegacyDefaultTtsPrefsPath, - writeTtsUserPrefsForMigration, - type TtsUserPrefs, -} from "../../../tts/tts-prefs-store.js"; +import path from "node:path"; +import { type TtsUserPrefs, writeTtsUserPrefsSnapshot } from "../../../tts/tts-prefs-store.js"; +import { resolveConfigDir } from "../../../utils.js"; + +function resolveLegacyDefaultTtsPrefsPath(env: NodeJS.ProcessEnv = process.env): string { + return path.join(resolveConfigDir(env), "settings", "tts.json"); +} function coerceTtsPrefs(value: unknown): TtsUserPrefs { return value && typeof value === "object" && !Array.isArray(value) ? (value as TtsUserPrefs) : {}; @@ -36,7 +38,7 @@ export async function importLegacyTtsPrefsFileToSqlite( } prefs = {}; } - writeTtsUserPrefsForMigration(prefs, env); + writeTtsUserPrefsSnapshot(prefs, env); await fs.rm(filePath, { force: true }).catch(() => undefined); return { imported: true }; } diff --git a/src/cron/store.test.ts b/src/cron/store.test.ts index 16dd052d900..29c0aa2f06a 100644 --- a/src/cron/store.test.ts +++ b/src/cron/store.test.ts @@ -6,15 +6,10 @@ import { importLegacyCronStateFileToSqlite, importLegacyCronStoreToSqlite, loadLegacyCronStoreForMigration, + resolveLegacyCronStorePath, } from "../commands/doctor/legacy/cron-store.js"; import { closeOpenClawStateDatabaseForTest } from "../state/openclaw-state-db.js"; -import { - loadCronStore, - loadCronStoreSync, - resolveLegacyCronStorePath, - saveCronStore, - updateCronStoreJobs, -} from "./store.js"; +import { loadCronStore, loadCronStoreSync, saveCronStore, updateCronStoreJobs } from "./store.js"; import type { CronStoreFile } from "./types.js"; let fixtureRoot = ""; diff --git a/src/cron/store.ts b/src/cron/store.ts index 7a44207809d..bbbab5c2130 100644 --- a/src/cron/store.ts +++ b/src/cron/store.ts @@ -1,13 +1,10 @@ -import path from "node:path"; import type { Insertable } from "kysely"; -import { expandHomePrefix } from "../infra/home-dir.js"; import { executeSqliteQuerySync, getNodeSqliteKysely } from "../infra/kysely-sync.js"; import type { DB as OpenClawStateKyselyDatabase } from "../state/openclaw-state-db.generated.js"; import { openOpenClawStateDatabase, runOpenClawStateWriteTransaction, } from "../state/openclaw-state-db.js"; -import { resolveConfigDir } from "../utils.js"; import { tryCronScheduleIdentity } from "./schedule-identity.js"; import type { CronJob, CronStoreFile } from "./types.js"; @@ -23,27 +20,19 @@ type CronJobRow = { state_json: string; }; -const DEFAULT_CRON_STORE_KEY = "default"; - -function resolveDefaultCronDir(): string { - return path.join(resolveConfigDir(), "cron"); -} - -function resolveDefaultLegacyCronStorePath(): string { - return path.join(resolveDefaultCronDir(), "jobs.json"); -} - -export type CronStateFileEntry = { +export type CronRuntimeStateEntry = { updatedAtMs?: number; scheduleIdentity?: string; state?: Record; }; -export type CronStateFile = { +export type CronRuntimeStateSnapshot = { version: 1; - jobs: Record; + jobs: Record; }; +const DEFAULT_CRON_STORE_KEY = "default"; + function cronStoreKey(storeKey: string): string { const normalized = storeKey.trim(); return normalized || DEFAULT_CRON_STORE_KEY; @@ -62,8 +51,8 @@ function stripRuntimeOnlyCronJobFields(job: CronJob): Record { return { ...rest, state: {} }; } -function extractStateFile(store: CronStoreFile): CronStateFile { - const jobs: Record = {}; +export function extractCronRuntimeStateSnapshot(store: CronStoreFile): CronRuntimeStateSnapshot { + const jobs: Record = {}; for (const job of store.jobs) { jobs[job.id] = { updatedAtMs: job.updatedAtMs, @@ -74,23 +63,10 @@ function extractStateFile(store: CronStoreFile): CronStateFile { return { version: 1, jobs }; } -export const extractCronStateFileForMigration = extractStateFile; - export function resolveCronStoreKey(): string { return DEFAULT_CRON_STORE_KEY; } -export function resolveLegacyCronStorePath(configuredLegacyStorePath?: string): string { - if (configuredLegacyStorePath?.trim()) { - const raw = configuredLegacyStorePath.trim(); - if (raw.startsWith("~")) { - return path.resolve(expandHomePrefix(raw)); - } - return path.resolve(raw); - } - return resolveDefaultLegacyCronStorePath(); -} - function ensureJobStateObject(job: CronStoreFile["jobs"][number]): void { if (!job.state || typeof job.state !== "object") { job.state = {} as never; @@ -116,7 +92,10 @@ function resolveUpdatedAtMs(job: CronStoreFile["jobs"][number], updatedAtMs: unk : Date.now(); } -function mergeStateFileEntry(job: CronStoreFile["jobs"][number], entry: CronStateFileEntry): void { +function mergeRuntimeStateSnapshotEntry( + job: CronStoreFile["jobs"][number], + entry: CronRuntimeStateEntry, +): void { job.updatedAtMs = resolveUpdatedAtMs(job, entry.updatedAtMs); job.state = (entry.state ?? {}) as never; if ( @@ -138,7 +117,7 @@ function parseCronStateJson(value: string): Record { } function mergeCronJobRowRuntimeState(job: CronStoreFile["jobs"][number], row: CronJobRow): void { - mergeStateFileEntry(job, { + mergeRuntimeStateSnapshotEntry(job, { updatedAtMs: row.runtime_updated_at_ms ?? undefined, scheduleIdentity: row.schedule_identity ?? undefined, state: parseCronStateJson(row.state_json), @@ -257,17 +236,16 @@ function writeCronJobsToSqlite(storeKey: string, store: CronStoreFile): void { }); } -export function writeCronJobsForMigration(storeKey: string, store: CronStoreFile): void { - writeCronJobsToSqlite(storeKey, store); -} - -function writeCronJobRuntimeStateToSqlite(storeKey: string, stateFile: CronStateFile): number { +export function writeCronRuntimeStateSnapshot( + storeKey: string, + stateSnapshot: CronRuntimeStateSnapshot, +): number { const normalizedStoreKey = cronStoreKey(storeKey); const updatedAt = Date.now(); let importedJobs = 0; runOpenClawStateWriteTransaction((database) => { const db = getCronJobsKysely(database.db); - for (const [jobId, entry] of Object.entries(stateFile.jobs)) { + for (const [jobId, entry] of Object.entries(stateSnapshot.jobs)) { const result = executeSqliteQuerySync( database.db, db @@ -293,13 +271,6 @@ function writeCronJobRuntimeStateToSqlite(storeKey: string, stateFile: CronState return importedJobs; } -export function writeCronJobRuntimeStateForMigration( - storeKey: string, - stateFile: CronStateFile, -): number { - return writeCronJobRuntimeStateToSqlite(storeKey, stateFile); -} - export async function saveCronStore( storeKey: string, store: CronStoreFile, @@ -307,7 +278,7 @@ export async function saveCronStore( ) { void opts?.skipBackup; if (opts?.stateOnly === true) { - writeCronJobRuntimeStateToSqlite(storeKey, extractStateFile(store)); + writeCronRuntimeStateSnapshot(storeKey, extractCronRuntimeStateSnapshot(store)); return; } writeCronJobsToSqlite(storeKey, store); diff --git a/src/infra/device-pairing.test.ts b/src/infra/device-pairing.test.ts index 1c2f23d2268..639319279f4 100644 --- a/src/infra/device-pairing.test.ts +++ b/src/infra/device-pairing.test.ts @@ -22,11 +22,16 @@ import { type PairedDevice, type RotateDeviceTokenResult, } from "./device-pairing.js"; -import { - readPairingStateRecord, - resolvePairingPaths, - writePairingStateRecord, -} from "./pairing-files.js"; +import { readPairingStateRecord, writePairingStateRecord } from "./pairing-files.js"; + +function resolveLegacyPairingFixturePaths(baseDir: string, subdir: string) { + const dir = path.join(baseDir, subdir); + return { + dir, + pendingPath: path.join(dir, "pending.json"), + pairedPath: path.join(dir, "paired.json"), + }; +} async function setupPairedOperatorDevice(baseDir: string, scopes: string[]) { const request = await requestDevicePairing( @@ -175,7 +180,7 @@ describe("device pairing tokens", () => { test("ignores legacy pairing state files at runtime", async () => { const baseDir = await makeDevicePairingDir(); - const paths = resolvePairingPaths(baseDir, "devices"); + const paths = resolveLegacyPairingFixturePaths(baseDir, "devices"); await mkdir(paths.dir, { recursive: true }); await writeFile(paths.pendingPath, "[]", "utf8"); await writeFile(paths.pairedPath, "[]", "utf8"); @@ -1425,7 +1430,7 @@ describe("device pairing tokens", () => { }, baseDir, ); - const { pairedPath } = resolvePairingPaths(baseDir, "devices"); + const { pairedPath } = resolveLegacyPairingFixturePaths(baseDir, "devices"); await mkdir(path.dirname(pairedPath), { recursive: true }); await writeFile(pairedPath, "{not-json}", "utf8"); diff --git a/src/infra/node-pairing.test.ts b/src/infra/node-pairing.test.ts index da2c33528fa..535bdf617a2 100644 --- a/src/infra/node-pairing.test.ts +++ b/src/infra/node-pairing.test.ts @@ -1,4 +1,5 @@ import fs from "node:fs/promises"; +import path from "node:path"; import { afterAll, beforeAll, describe, expect, test } from "vitest"; import { createSuiteTempRootTracker } from "../test-helpers/temp-dir.js"; import { @@ -10,7 +11,16 @@ import { updatePairedNodeMetadata, verifyNodeToken, } from "./node-pairing.js"; -import { readPairingStateRecord, resolvePairingPaths } from "./pairing-files.js"; +import { readPairingStateRecord } from "./pairing-files.js"; + +function resolveLegacyPairingFixturePaths(baseDir: string, subdir: string) { + const dir = path.join(baseDir, subdir); + return { + dir, + pendingPath: path.join(dir, "pending.json"), + pairedPath: path.join(dir, "paired.json"), + }; +} async function setupPairedNode(baseDir: string): Promise { const request = await requestNodePairing( @@ -131,7 +141,7 @@ describe("node pairing tokens", () => { test("ignores legacy pairing state files at runtime", async () => { await withNodePairingDir(async (baseDir) => { - const paths = resolvePairingPaths(baseDir, "nodes"); + const paths = resolveLegacyPairingFixturePaths(baseDir, "nodes"); await fs.mkdir(paths.dir, { recursive: true }); await fs.writeFile(paths.pendingPath, "[]", "utf8"); await fs.writeFile(paths.pairedPath, "[]", "utf8"); @@ -268,7 +278,7 @@ describe("node pairing tokens", () => { test("ignores corrupt legacy paired node state when requesting pairing", async () => { await withNodePairingDir(async (baseDir) => { - const { dir, pairedPath } = resolvePairingPaths(baseDir, "nodes"); + const { dir, pairedPath } = resolveLegacyPairingFixturePaths(baseDir, "nodes"); await fs.mkdir(dir, { recursive: true }); await fs.writeFile(pairedPath, "{not-json}", "utf8"); diff --git a/src/infra/pairing-files.ts b/src/infra/pairing-files.ts index 466046fa320..1e0cad8f6ee 100644 --- a/src/infra/pairing-files.ts +++ b/src/infra/pairing-files.ts @@ -1,5 +1,3 @@ -import path from "node:path"; -import { resolveStateDir } from "../config/paths.js"; import type { OpenClawStateDatabaseOptions } from "../state/openclaw-state-db.js"; import { readOpenClawStateKvJson, @@ -11,16 +9,6 @@ export { createAsyncLock } from "./json-files.js"; const PAIRING_STATE_SCOPE_PREFIX = "pairing"; -export function resolvePairingPaths(baseDir: string | undefined, subdir: string) { - const root = baseDir ?? resolveStateDir(); - const dir = path.join(root, subdir); - return { - dir, - pendingPath: path.join(dir, "pending.json"), - pairedPath: path.join(dir, "paired.json"), - }; -} - function sqliteOptionsForBaseDir(baseDir: string | undefined): OpenClawStateDatabaseOptions { return baseDir ? { env: { ...process.env, OPENCLAW_STATE_DIR: baseDir } } : {}; } diff --git a/src/tts/status-config.test.ts b/src/tts/status-config.test.ts index 2c86205518e..edd911a1da2 100644 --- a/src/tts/status-config.test.ts +++ b/src/tts/status-config.test.ts @@ -5,7 +5,7 @@ import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/types.js"; import { resetPluginStateStoreForTests } from "../plugin-state/plugin-state-store.js"; import { resolveStatusTtsSnapshot } from "./status-config.js"; -import { writeTtsUserPrefsForMigration } from "./tts-prefs-store.js"; +import { writeTtsUserPrefsSnapshot } from "./tts-prefs-store.js"; let fixtureRoot = ""; let fixtureId = 0; @@ -54,7 +54,7 @@ function restoreEnv(key: string, value: string | undefined): void { describe("resolveStatusTtsSnapshot", () => { it("uses prefs overrides without loading speech providers", async () => { await withStatusTempHome(async () => { - writeTtsUserPrefsForMigration({ + writeTtsUserPrefsSnapshot({ tts: { auto: "always", provider: "edge", @@ -283,7 +283,7 @@ describe("resolveStatusTtsSnapshot", () => { it("uses provider metadata for local provider prefs overrides", async () => { await withStatusTempHome(async () => { - writeTtsUserPrefsForMigration({ + writeTtsUserPrefsSnapshot({ tts: { auto: "always", provider: "edge", @@ -326,7 +326,7 @@ describe("resolveStatusTtsSnapshot", () => { delete process.env.OPENCLAW_STATE_DIR; vi.stubEnv("OPENCLAW_CONFIG_PATH", path.join(stateDir, "openclaw.json")); try { - writeTtsUserPrefsForMigration({ + writeTtsUserPrefsSnapshot({ tts: { auto: "always", provider: "openai", diff --git a/src/tts/tts-config.test.ts b/src/tts/tts-config.test.ts index 3e05b9b84d4..1b0aad33dd5 100644 --- a/src/tts/tts-config.test.ts +++ b/src/tts/tts-config.test.ts @@ -9,7 +9,7 @@ import { resolveEffectiveTtsConfig, shouldAttemptTtsPayload, } from "./tts-config.js"; -import { writeTtsUserPrefsForMigration } from "./tts-prefs-store.js"; +import { writeTtsUserPrefsSnapshot } from "./tts-prefs-store.js"; describe("shouldAttemptTtsPayload", () => { let originalStateDir: string | undefined; @@ -59,7 +59,7 @@ describe("shouldAttemptTtsPayload", () => { }); it("honors session auto state before prefs and config", () => { - writeTtsUserPrefsForMigration({ tts: { auto: "off" } }); + writeTtsUserPrefsSnapshot({ tts: { auto: "off" } }); const cfg = { messages: { tts: { auto: "off" } } } as OpenClawConfig; expect(shouldAttemptTtsPayload({ cfg, ttsAuto: "always" })).toBe(true); @@ -69,10 +69,10 @@ describe("shouldAttemptTtsPayload", () => { it("uses local prefs before config auto mode", () => { const cfg = { messages: { tts: { auto: "off" } } } as OpenClawConfig; - writeTtsUserPrefsForMigration({ tts: { enabled: true } }); + writeTtsUserPrefsSnapshot({ tts: { enabled: true } }); expect(shouldAttemptTtsPayload({ cfg })).toBe(true); - writeTtsUserPrefsForMigration({ tts: { auto: "off" } }); + writeTtsUserPrefsSnapshot({ tts: { auto: "off" } }); expect( shouldAttemptTtsPayload({ cfg: { messages: { tts: { enabled: true } } } as OpenClawConfig }), ).toBe(false); diff --git a/src/tts/tts-prefs-store.ts b/src/tts/tts-prefs-store.ts index ca6e8d5501c..913b3e8456c 100644 --- a/src/tts/tts-prefs-store.ts +++ b/src/tts/tts-prefs-store.ts @@ -1,7 +1,5 @@ -import path from "node:path"; import type { TtsAutoMode, TtsProvider } from "../config/types.tts.js"; import { createPluginStateSyncKeyedStore } from "../plugin-state/plugin-state-store.js"; -import { resolveConfigDir } from "../utils.js"; const TTS_PREFS_PLUGIN_ID = "speech-core"; const TTS_PREFS_NAMESPACE = "tts-prefs"; @@ -40,10 +38,6 @@ export function isSqliteTtsPrefsRef(value: string): boolean { return value === SQLITE_TTS_PREFS_REF; } -export function resolveLegacyDefaultTtsPrefsPath(env: NodeJS.ProcessEnv = process.env): string { - return path.join(resolveConfigDir(env), "settings", "tts.json"); -} - export function resolveTtsPrefsRef( _prefsPath?: string, _env: NodeJS.ProcessEnv = process.env, @@ -58,7 +52,7 @@ export function readTtsUserPrefs( return coercePrefs(openTtsPrefsStore(env).lookup(TTS_PREFS_KEY)); } -export function writeTtsUserPrefsForMigration( +export function writeTtsUserPrefsSnapshot( prefs: TtsUserPrefs, env: NodeJS.ProcessEnv = process.env, ): void {