From d1cc22033c30115ea5ca34f7bb1ea98693188e5f Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 8 May 2026 21:18:51 +0100 Subject: [PATCH] refactor: remove state file escape hatches --- src/infra/device-identity.test.ts | 2 ++ src/infra/device-identity.ts | 36 +++++++++++++++---- .../installed-plugin-index-persisted-read.ts | 14 ++------ src/plugins/installed-plugin-index-records.ts | 1 - .../installed-plugin-index-store-path.ts | 4 --- src/plugins/installed-plugin-index-store.ts | 12 ------- 6 files changed, 34 insertions(+), 35 deletions(-) diff --git a/src/infra/device-identity.test.ts b/src/infra/device-identity.test.ts index 203680234c3..a901b948578 100644 --- a/src/infra/device-identity.test.ts +++ b/src/infra/device-identity.test.ts @@ -38,6 +38,8 @@ describe("device identity crypto helpers", () => { const created = loadOrCreateDeviceIdentity(identityPath); expect(loadDeviceIdentityIfPresent(identityPath)).toEqual(created); + expect(fs.existsSync(identityPath)).toBe(false); + expect(fs.existsSync(`${identityPath}.sqlite`)).toBe(false); }); }); diff --git a/src/infra/device-identity.ts b/src/infra/device-identity.ts index 24973c091bb..27caccb54d0 100644 --- a/src/infra/device-identity.ts +++ b/src/infra/device-identity.ts @@ -2,7 +2,6 @@ import crypto from "node:crypto"; import fs from "node:fs"; import path from "node:path"; import { resolveStateDir } from "../config/paths.js"; -import type { OpenClawStateDatabaseOptions } from "../state/openclaw-state-db.js"; import { readOpenClawStateKvJson, writeOpenClawStateKvJson, @@ -32,6 +31,7 @@ type StoredSwiftIdentity = { const DEVICE_IDENTITY_SCOPE = "identity.device"; const DEVICE_IDENTITY_KEY = "default"; +const DEVICE_IDENTITY_PATH_KEY_PREFIX = "path:"; function resolveDefaultIdentityPath(): string { return path.join(resolveStateDir(), "identity", "device.json"); @@ -41,8 +41,9 @@ function resolveIdentityPathForEnv(env: NodeJS.ProcessEnv = process.env): string return path.join(resolveStateDir(env), "identity", "device.json"); } -function sqliteOptionsForIdentityPath(filePath: string): OpenClawStateDatabaseOptions { +function stateDbOptionsForIdentityPath(filePath: string): { env: NodeJS.ProcessEnv } { const resolved = path.resolve(filePath); + const envStateDir = resolveStateDir(process.env); if (resolved.endsWith(path.join("identity", "device.json"))) { return { env: { @@ -51,7 +52,28 @@ function sqliteOptionsForIdentityPath(filePath: string): OpenClawStateDatabaseOp }, }; } - return { path: `${resolved}.sqlite` }; + if (resolved.startsWith(`${path.resolve(envStateDir)}${path.sep}`)) { + return { + env: { + ...process.env, + OPENCLAW_STATE_DIR: envStateDir, + }, + }; + } + return { + env: { + ...process.env, + OPENCLAW_STATE_DIR: path.dirname(resolved), + }, + }; +} + +function identityKeyForPath(filePath: string): string { + const resolved = path.resolve(filePath); + if (resolved.endsWith(path.join("identity", "device.json"))) { + return DEVICE_IDENTITY_KEY; + } + return `${DEVICE_IDENTITY_PATH_KEY_PREFIX}${crypto.createHash("sha256").update(resolved).digest("hex")}`; } const ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex"); @@ -171,8 +193,8 @@ function readStoredIdentity(filePath: string): StoredIdentity | null { return parseStoredIdentity( readOpenClawStateKvJson( DEVICE_IDENTITY_SCOPE, - DEVICE_IDENTITY_KEY, - sqliteOptionsForIdentityPath(filePath), + identityKeyForPath(filePath), + stateDbOptionsForIdentityPath(filePath), ), ); } @@ -186,9 +208,9 @@ function readStoredIdentityForEnv(env: NodeJS.ProcessEnv): StoredIdentity | null function writeStoredIdentity(filePath: string, stored: StoredIdentity): void { writeOpenClawStateKvJson( DEVICE_IDENTITY_SCOPE, - DEVICE_IDENTITY_KEY, + identityKeyForPath(filePath), stored as unknown as OpenClawStateJsonValue, - sqliteOptionsForIdentityPath(filePath), + stateDbOptionsForIdentityPath(filePath), ); } diff --git a/src/plugins/installed-plugin-index-persisted-read.ts b/src/plugins/installed-plugin-index-persisted-read.ts index c9e9d055acc..8e8b271d669 100644 --- a/src/plugins/installed-plugin-index-persisted-read.ts +++ b/src/plugins/installed-plugin-index-persisted-read.ts @@ -1,13 +1,10 @@ import { z } from "zod"; -import { tryReadJson, tryReadJsonSync } from "../infra/json-files.js"; +import { tryReadJsonSync } from "../infra/json-files.js"; import { isBlockedObjectKey } from "../infra/prototype-keys.js"; import { readOpenClawStateKvJson } from "../state/openclaw-state-kv.js"; import { safeParseWithSchema } from "../utils/zod-parse.js"; import { extractPluginInstallRecordsFromInstalledPluginIndex } from "./installed-plugin-index-install-records.js"; -import { - resolveInstalledPluginIndexStorePath, - type InstalledPluginIndexStoreOptions, -} from "./installed-plugin-index-store-path.js"; +import { type InstalledPluginIndexStoreOptions } from "./installed-plugin-index-store-path.js"; import { INSTALLED_PLUGIN_INDEX_MIGRATION_VERSION, INSTALLED_PLUGIN_INDEX_VERSION, @@ -153,9 +150,6 @@ export function resolveInstalledPluginIndexStateDbOptions( function readPersistedInstalledPluginIndexJsonSync( options: InstalledPluginIndexStoreOptions, ): unknown { - if (options.filePath) { - return tryReadJsonSync(resolveInstalledPluginIndexStorePath(options)); - } try { return readOpenClawStateKvJson( INSTALLED_PLUGIN_INDEX_KV_SCOPE, @@ -170,9 +164,7 @@ function readPersistedInstalledPluginIndexJsonSync( export async function readPersistedInstalledPluginIndex( options: InstalledPluginIndexStoreOptions = {}, ): Promise { - const parsed = options.filePath - ? await tryReadJson(resolveInstalledPluginIndexStorePath(options)) - : readPersistedInstalledPluginIndexJsonSync(options); + const parsed = readPersistedInstalledPluginIndexJsonSync(options); return parseInstalledPluginIndex(parsed); } diff --git a/src/plugins/installed-plugin-index-records.ts b/src/plugins/installed-plugin-index-records.ts index 9140a5f8107..7b9ec7f7996 100644 --- a/src/plugins/installed-plugin-index-records.ts +++ b/src/plugins/installed-plugin-index-records.ts @@ -26,7 +26,6 @@ export const PLUGIN_INSTALLS_CONFIG_PATH = ["plugins", "installs"] as const; export type InstalledPluginIndexRecordStoreOptions = { env?: NodeJS.ProcessEnv; stateDir?: string; - filePath?: string; }; type InstalledPluginIndexRecordRefreshOptions = InstalledPluginIndexRecordStoreOptions & diff --git a/src/plugins/installed-plugin-index-store-path.ts b/src/plugins/installed-plugin-index-store-path.ts index 72001a41092..08cf99efaad 100644 --- a/src/plugins/installed-plugin-index-store-path.ts +++ b/src/plugins/installed-plugin-index-store-path.ts @@ -6,15 +6,11 @@ const INSTALLED_PLUGIN_INDEX_STORE_PATH = path.join("plugins", "installs.json"); export type InstalledPluginIndexStoreOptions = { env?: NodeJS.ProcessEnv; stateDir?: string; - filePath?: string; }; export function resolveInstalledPluginIndexStorePath( options: InstalledPluginIndexStoreOptions = {}, ): string { - if (options.filePath) { - return options.filePath; - } const env = options.env ?? process.env; const stateDir = options.stateDir ?? resolveStateDir(env); return path.join(stateDir, INSTALLED_PLUGIN_INDEX_STORE_PATH); diff --git a/src/plugins/installed-plugin-index-store.ts b/src/plugins/installed-plugin-index-store.ts index b33c7eafd5c..43cbdf7dda9 100644 --- a/src/plugins/installed-plugin-index-store.ts +++ b/src/plugins/installed-plugin-index-store.ts @@ -95,18 +95,6 @@ export function writePersistedInstalledPluginIndexSync( export function deletePersistedInstalledPluginIndexSync( options: InstalledPluginIndexStoreOptions = {}, ): boolean { - if (options.filePath) { - try { - fs.unlinkSync(resolveInstalledPluginIndexStorePath(options)); - clearCurrentPluginMetadataSnapshotState(); - return true; - } catch (err) { - if ((err as NodeJS.ErrnoException)?.code !== "ENOENT") { - throw err; - } - return false; - } - } const removed = deleteOpenClawStateKvJson( INSTALLED_PLUGIN_INDEX_KV_SCOPE, INSTALLED_PLUGIN_INDEX_KV_KEY,