refactor: keep exec approvals legacy path in doctor

This commit is contained in:
Peter Steinberger
2026-05-10 01:16:53 +01:00
parent c99af7ca36
commit d90bece2a2
5 changed files with 26 additions and 22 deletions

View File

@@ -201,7 +201,7 @@ The branch already has a real shared SQLite base:
no longer reads or rewrites the legacy workspace marker.
- Exec approvals now live in shared SQLite KV (`exec.approvals/current`).
Doctor imports legacy `~/.openclaw/exec-approvals.json`; runtime writes no
longer create or rewrite that file.
longer create, rewrite, or report that file as its active store location.
- Device identity, device auth, and bootstrap runtime modules now keep their
SQLite snapshot readers/writers separate from doctor-only legacy JSON import
helpers.

View File

@@ -2,15 +2,12 @@ import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
loadExecApprovals,
resolveExecApprovalsPath,
type ExecApprovalsFile,
} from "../../../infra/exec-approvals.js";
import { loadExecApprovals, type ExecApprovalsFile } from "../../../infra/exec-approvals.js";
import { resetPluginStateStoreForTests } from "../../../plugin-state/plugin-state-store.js";
import {
importLegacyExecApprovalsFileToSqlite,
legacyExecApprovalsFileExists,
resolveLegacyExecApprovalsPath,
} from "./exec-approvals.js";
const tempDirs: string[] = [];
@@ -49,7 +46,7 @@ function createHomeDir(): string {
}
function writeApprovalsFile(homeDir: string, file: ExecApprovalsFile): string {
const approvalsPath = resolveExecApprovalsPath({
const approvalsPath = resolveLegacyExecApprovalsPath({
...process.env,
OPENCLAW_HOME: homeDir,
});

View File

@@ -1,5 +1,5 @@
import fs from "node:fs";
import { resolveExecApprovalsPath } from "../../../infra/exec-approvals.js";
import { expandHomePrefix } from "../../../infra/home-dir.js";
import type { OpenClawStateDatabaseOptions } from "../../../state/openclaw-state-db.js";
import {
writeOpenClawStateKvJson,
@@ -8,6 +8,7 @@ import {
const EXEC_APPROVALS_KV_SCOPE = "exec.approvals";
const EXEC_APPROVALS_KV_KEY = "current";
const LEGACY_EXEC_APPROVALS_FILE = "~/.openclaw/exec-approvals.json";
function sqliteOptionsForEnv(env: NodeJS.ProcessEnv): OpenClawStateDatabaseOptions {
return { env };
@@ -18,13 +19,17 @@ function readLegacyExecApprovalsRaw(env: NodeJS.ProcessEnv = process.env): {
exists: boolean;
path: string;
} {
const filePath = resolveExecApprovalsPath(env);
const filePath = resolveLegacyExecApprovalsPath(env);
if (!fs.existsSync(filePath)) {
return { raw: null, exists: false, path: filePath };
}
return { raw: fs.readFileSync(filePath, "utf8"), exists: true, path: filePath };
}
export function resolveLegacyExecApprovalsPath(env: NodeJS.ProcessEnv = process.env): string {
return expandHomePrefix(LEGACY_EXEC_APPROVALS_FILE, { env });
}
export function legacyExecApprovalsFileExists(env: NodeJS.ProcessEnv = process.env): boolean {
return readLegacyExecApprovalsRaw(env).exists;
}

View File

@@ -26,7 +26,7 @@ let readExecApprovalsSnapshot: ExecApprovalsModule["readExecApprovalsSnapshot"];
let recordAllowlistMatchesUse: ExecApprovalsModule["recordAllowlistMatchesUse"];
let recordAllowlistUse: ExecApprovalsModule["recordAllowlistUse"];
let requestExecApprovalViaSocket: ExecApprovalsModule["requestExecApprovalViaSocket"];
let resolveExecApprovalsPath: ExecApprovalsModule["resolveExecApprovalsPath"];
let resolveExecApprovalsStoreLocationForDisplay: ExecApprovalsModule["resolveExecApprovalsStoreLocationForDisplay"];
let resolveExecApprovalsSocketPath: ExecApprovalsModule["resolveExecApprovalsSocketPath"];
let saveExecApprovals: ExecApprovalsModule["saveExecApprovals"];
@@ -47,7 +47,7 @@ beforeAll(async () => {
recordAllowlistMatchesUse,
recordAllowlistUse,
requestExecApprovalViaSocket,
resolveExecApprovalsPath,
resolveExecApprovalsStoreLocationForDisplay,
resolveExecApprovalsSocketPath,
saveExecApprovals,
} = await import("./exec-approvals.js"));
@@ -119,12 +119,13 @@ function expectAllowlistEntryFields(
}
describe("exec approvals store helpers", () => {
it("expands home-prefixed default file and socket paths for compatibility labels", () => {
it("reports the SQLite store location and expands the socket path", () => {
const dir = createHomeDir();
expect(path.normalize(resolveExecApprovalsPath())).toBe(
path.normalize(path.join(dir, ".openclaw", "exec-approvals.json")),
expect(resolveExecApprovalsStoreLocationForDisplay()).toContain(
path.join(process.env.OPENCLAW_STATE_DIR ?? "", "state", "openclaw.sqlite"),
);
expect(resolveExecApprovalsStoreLocationForDisplay()).toContain("#kv/exec.approvals/current");
expect(path.normalize(resolveExecApprovalsSocketPath())).toBe(
path.normalize(path.join(dir, ".openclaw", "exec-approvals.sock")),
);
@@ -168,7 +169,7 @@ describe("exec approvals store helpers", () => {
expect(missing.exists).toBe(false);
expect(missing.raw).toBeNull();
expect(missing.file).toEqual(normalizeExecApprovals({ version: 1, agents: {} }));
expect(path.normalize(missing.path)).toBe(path.normalize(approvalsFilePath(dir)));
expect(missing.path).toBe(resolveExecApprovalsStoreLocationForDisplay());
fs.mkdirSync(path.dirname(approvalsFilePath(dir)), { recursive: true });
fs.writeFileSync(approvalsFilePath(dir), "{invalid", "utf8");

View File

@@ -7,6 +7,7 @@ import {
readStringValue,
} from "../shared/string-coerce.js";
import type { OpenClawStateDatabaseOptions } from "../state/openclaw-state-db.js";
import { resolveOpenClawStateSqlitePath } from "../state/openclaw-state-db.paths.js";
import {
deleteOpenClawStateKvJson,
readOpenClawStateKvJson,
@@ -211,7 +212,6 @@ const DEFAULT_ASK: ExecAsk = "off";
export const DEFAULT_EXEC_APPROVAL_ASK_FALLBACK: ExecSecurity = "full";
const DEFAULT_AUTO_ALLOW_SKILLS = false;
const DEFAULT_SOCKET = "~/.openclaw/exec-approvals.sock";
const DEFAULT_FILE = "~/.openclaw/exec-approvals.json";
const EXEC_APPROVALS_KV_SCOPE = "exec.approvals";
const EXEC_APPROVALS_KV_KEY = "current";
@@ -222,8 +222,10 @@ function hashExecApprovalsRaw(raw: string | null): string {
.digest("hex");
}
export function resolveExecApprovalsPath(env: NodeJS.ProcessEnv = process.env): string {
return expandHomePrefix(DEFAULT_FILE, { env });
export function resolveExecApprovalsStoreLocationForDisplay(
env: NodeJS.ProcessEnv = process.env,
): string {
return `${resolveOpenClawStateSqlitePath(env)}#kv/${EXEC_APPROVALS_KV_SCOPE}/${EXEC_APPROVALS_KV_KEY}`;
}
export function resolveExecApprovalsSocketPath(env: NodeJS.ProcessEnv = process.env): string {
@@ -465,10 +467,9 @@ function parseExecApprovalsRaw(raw: string | null): ExecApprovalsFile {
}
export function readExecApprovalsSnapshot(): ExecApprovalsSnapshot {
const filePath = resolveExecApprovalsPath();
const sqliteRaw = readExecApprovalsRawFromSqlite();
return {
path: filePath,
path: resolveExecApprovalsStoreLocationForDisplay(),
exists: sqliteRaw !== null,
raw: sqliteRaw,
file: parseExecApprovalsRaw(sqliteRaw),
@@ -655,7 +656,7 @@ export function resolveExecApprovals(
file,
agentId,
overrides,
path: resolveExecApprovalsPath(),
path: resolveExecApprovalsStoreLocationForDisplay(),
socketPath: expandHomePrefix(file.socket?.path ?? resolveExecApprovalsSocketPath()),
token: file.socket?.token ?? "",
});
@@ -731,7 +732,7 @@ export function resolveExecApprovalsFromFile(params: {
...(Array.isArray(agent.allowlist) ? agent.allowlist : []),
];
return {
path: params.path ?? resolveExecApprovalsPath(),
path: params.path ?? resolveExecApprovalsStoreLocationForDisplay(),
socketPath: expandHomePrefix(
params.socketPath ?? file.socket?.path ?? resolveExecApprovalsSocketPath(),
),