fix(config): honor isolated state-dir config writes

This commit is contained in:
Peter Steinberger
2026-04-03 12:40:43 +01:00
parent 51d6d7013f
commit 36a233ff98
2 changed files with 42 additions and 7 deletions

View File

@@ -38,7 +38,7 @@ import {
materializeRuntimeConfig,
} from "./materialize.js";
import { applyMergePatch } from "./merge-patch.js";
import { resolveConfigPath, resolveDefaultConfigCandidates, resolveStateDir } from "./paths.js";
import { resolveConfigPath, resolveStateDir } from "./paths.js";
import { isBlockedObjectKey } from "./prototype-keys.js";
import { applyConfigOverrides } from "./runtime-overrides.js";
import type { OpenClawConfig, ConfigFileSnapshot, LegacyConfigIssue } from "./types.js";
@@ -1673,12 +1673,7 @@ async function finalizeReadConfigSnapshotInternalResult(
export function createConfigIO(overrides: ConfigIoDeps = {}) {
const deps = normalizeDeps(overrides);
const requestedConfigPath = resolveConfigPathForDeps(deps);
const candidatePaths = deps.configPath
? [requestedConfigPath]
: resolveDefaultConfigCandidates(deps.env, deps.homedir);
const configPath =
candidatePaths.find((candidate) => deps.fs.existsSync(candidate)) ?? requestedConfigPath;
const configPath = resolveConfigPathForDeps(deps);
function observeLoadConfigSnapshot(snapshot: ConfigFileSnapshot): ConfigFileSnapshot {
observeConfigSnapshotSync(deps, snapshot);

View File

@@ -195,6 +195,46 @@ describe("config io write", () => {
},
);
it("keeps writes inside an OPENCLAW_STATE_DIR override even when the real home config exists", async () => {
await withSuiteHome(async (home) => {
const liveConfigPath = path.join(home, ".openclaw", "openclaw.json");
await fs.mkdir(path.dirname(liveConfigPath), { recursive: true });
await fs.writeFile(
liveConfigPath,
`${JSON.stringify({ gateway: { mode: "local", port: 18789 } }, null, 2)}\n`,
"utf-8",
);
const overrideDir = path.join(home, "isolated-state");
const env = { OPENCLAW_STATE_DIR: overrideDir } as NodeJS.ProcessEnv;
const io = createConfigIO({
env,
homedir: () => home,
logger: silentLogger,
});
expect(io.configPath).toBe(path.join(overrideDir, "openclaw.json"));
await io.writeConfigFile({
agents: { list: [{ id: "main", default: true }] },
gateway: { mode: "local" },
session: { mainKey: "main", store: path.join(overrideDir, "sessions.json") },
});
const livePersisted = JSON.parse(await fs.readFile(liveConfigPath, "utf-8")) as {
gateway?: { mode?: unknown; port?: unknown };
};
expect(livePersisted.gateway).toEqual({ mode: "local", port: 18789 });
const overridePersisted = JSON.parse(
await fs.readFile(path.join(overrideDir, "openclaw.json"), "utf-8"),
) as {
session?: { store?: unknown };
};
expect(overridePersisted.session?.store).toBe(path.join(overrideDir, "sessions.json"));
});
});
it('shows actionable guidance for dmPolicy="open" without wildcard allowFrom', async () => {
await withSuiteHome(async (home) => {
const io = createConfigIO({