refactor: remove state file escape hatches

This commit is contained in:
Peter Steinberger
2026-05-08 21:18:51 +01:00
parent 9636089d04
commit d1cc22033c
6 changed files with 34 additions and 35 deletions

View File

@@ -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);
});
});

View File

@@ -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<OpenClawStateJsonValue>(
DEVICE_IDENTITY_SCOPE,
DEVICE_IDENTITY_KEY,
identityKeyForPath(filePath),
stored as unknown as OpenClawStateJsonValue,
sqliteOptionsForIdentityPath(filePath),
stateDbOptionsForIdentityPath(filePath),
);
}

View File

@@ -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<InstalledPluginIndex | null> {
const parsed = options.filePath
? await tryReadJson<unknown>(resolveInstalledPluginIndexStorePath(options))
: readPersistedInstalledPluginIndexJsonSync(options);
const parsed = readPersistedInstalledPluginIndexJsonSync(options);
return parseInstalledPluginIndex(parsed);
}

View File

@@ -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 &

View File

@@ -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);

View File

@@ -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,