mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:30:42 +00:00
fix(config): avoid env-ref reload restarts
This commit is contained in:
@@ -12,6 +12,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- Browser/tool: keep explicit AI snapshots from inheriting the efficient role-snapshot default and preserve numeric Playwright AI refs, so `--format ai` remains a real AI snapshot path. Fixes #62550. Thanks @ly85206559.
|
||||
- Gateway/config: keep in-process config patch reload comparisons on the resolved source snapshot when `${VAR}` env refs are restored on disk, avoiding false full gateway restarts for unchanged gateway/plugin secrets. Fixes #71208. Thanks @robbiethompson18.
|
||||
- Slack/messages: serialize write-client requests and whole outbound sends per target so rapid multi-message Slack replies preserve send order. Fixes #69101. (#69105) Thanks @nightq and @ztexydt-cqh.
|
||||
- Slack/messages: keep Slack bot tokens out of internal message-ordering and DM cache keys.
|
||||
- Slack/exec approvals: resolve native approval button clicks over the Gateway instead of delivering `/approve ...` as plain agent text, preserving retry buttons if Gateway resolution fails. Fixes #71023. (#71025) Thanks @marusan03.
|
||||
|
||||
@@ -2050,7 +2050,6 @@ export async function writeConfigFile(
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const committedSourceConfig = writeResult.persistedConfig ?? nextCfg;
|
||||
const notifyCommittedWrite = () => {
|
||||
const currentRuntimeConfig = getRuntimeConfigSnapshotState();
|
||||
if (!currentRuntimeConfig) {
|
||||
@@ -2058,7 +2057,7 @@ export async function writeConfigFile(
|
||||
}
|
||||
notifyRuntimeConfigWriteListeners({
|
||||
configPath: io.configPath,
|
||||
sourceConfig: committedSourceConfig,
|
||||
sourceConfig: nextCfg,
|
||||
runtimeConfig: currentRuntimeConfig,
|
||||
persistedHash: writeResult.persistedHash,
|
||||
writtenAtMs: Date.now(),
|
||||
@@ -2067,7 +2066,7 @@ export async function writeConfigFile(
|
||||
// Keep the last-known-good runtime snapshot active until the specialized refresh path
|
||||
// succeeds, so concurrent readers do not observe unresolved SecretRefs mid-refresh.
|
||||
await finalizeRuntimeSnapshotWrite({
|
||||
nextSourceConfig: committedSourceConfig,
|
||||
nextSourceConfig: nextCfg,
|
||||
hadRuntimeSnapshot,
|
||||
hadBothSnapshots,
|
||||
loadFreshConfig: () => io.loadConfig(),
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { PluginManifestRegistry } from "../plugins/manifest-registry.js";
|
||||
import { createSuiteTempRootTracker } from "../test-helpers/temp-dir.js";
|
||||
import {
|
||||
createConfigIO,
|
||||
registerConfigWriteListener,
|
||||
resetConfigRuntimeState,
|
||||
setRuntimeConfigSnapshot,
|
||||
writeConfigFile,
|
||||
@@ -532,4 +533,92 @@ describe("config io write", () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("notifies in-process reloaders with resolved source config when persisted env refs are restored", async () => {
|
||||
await withSuiteHome(async (home) => {
|
||||
const configPath = path.join(home, ".openclaw", "openclaw.json");
|
||||
const previousConfigPath = process.env.OPENCLAW_CONFIG_PATH;
|
||||
const previousGatewayToken = process.env.OPENCLAW_GATEWAY_TOKEN;
|
||||
process.env.OPENCLAW_CONFIG_PATH = configPath;
|
||||
process.env.OPENCLAW_GATEWAY_TOKEN = "gateway-token-runtime";
|
||||
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
||||
await fs.writeFile(
|
||||
configPath,
|
||||
`${JSON.stringify(
|
||||
{
|
||||
gateway: {
|
||||
mode: "local",
|
||||
auth: { mode: "token", token: "${OPENCLAW_GATEWAY_TOKEN}" },
|
||||
},
|
||||
agents: { defaults: { model: { primary: "openai/gpt-5.4" } } },
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
"utf-8",
|
||||
);
|
||||
const observedSources: unknown[] = [];
|
||||
const unsubscribe = registerConfigWriteListener((event) => {
|
||||
observedSources.push(event.sourceConfig);
|
||||
});
|
||||
|
||||
try {
|
||||
setRuntimeConfigSnapshot(
|
||||
{
|
||||
gateway: {
|
||||
mode: "local",
|
||||
auth: { mode: "token", token: "gateway-token-runtime" },
|
||||
},
|
||||
agents: { defaults: { model: { primary: "openai/gpt-5.4" } } },
|
||||
},
|
||||
{
|
||||
gateway: {
|
||||
mode: "local",
|
||||
auth: { mode: "token", token: "gateway-token-runtime" },
|
||||
},
|
||||
agents: { defaults: { model: { primary: "openai/gpt-5.4" } } },
|
||||
},
|
||||
);
|
||||
|
||||
await writeConfigFile({
|
||||
gateway: {
|
||||
mode: "local",
|
||||
auth: { mode: "token", token: "gateway-token-runtime" },
|
||||
},
|
||||
agents: { defaults: { model: { primary: "openrouter/anthropic/claude-sonnet-4.6" } } },
|
||||
});
|
||||
|
||||
expect(JSON.parse(await fs.readFile(configPath, "utf-8"))).toMatchObject({
|
||||
gateway: {
|
||||
auth: { token: "${OPENCLAW_GATEWAY_TOKEN}" },
|
||||
},
|
||||
});
|
||||
expect(observedSources).toEqual([
|
||||
expect.objectContaining({
|
||||
gateway: {
|
||||
mode: "local",
|
||||
auth: { mode: "token", token: "gateway-token-runtime" },
|
||||
},
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: "openrouter/anthropic/claude-sonnet-4.6" },
|
||||
},
|
||||
},
|
||||
}),
|
||||
]);
|
||||
} finally {
|
||||
unsubscribe();
|
||||
if (previousConfigPath === undefined) {
|
||||
delete process.env.OPENCLAW_CONFIG_PATH;
|
||||
} else {
|
||||
process.env.OPENCLAW_CONFIG_PATH = previousConfigPath;
|
||||
}
|
||||
if (previousGatewayToken === undefined) {
|
||||
delete process.env.OPENCLAW_GATEWAY_TOKEN;
|
||||
} else {
|
||||
process.env.OPENCLAW_GATEWAY_TOKEN = previousGatewayToken;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user