


























@@ -411,6 +411,20 @@ describe("buildGatewayReloadPlan", () => {
411411expect(plan.noopPaths).toStrictEqual([]);
412412});
413413414+it("hot-reloads auth cooldown changes without a gateway restart", () => {
415+const changedPaths = [
416+"auth.cooldowns.billingBackoffHours",
417+"auth.cooldowns.billingBackoffHoursByProvider.anthropic",
418+];
419+const plan = buildGatewayReloadPlan(changedPaths);
420+421+expect(plan.restartGateway).toBe(false);
422+expect(plan.restartReasons).toStrictEqual([]);
423+expect(plan.hotReasons).toStrictEqual(changedPaths);
424+expect(plan.noopPaths).toStrictEqual([]);
425+expect(resolveConfigReloadMetadata("auth.cooldowns.billingBackoffHours").kind).toBe("hot");
426+});
427+414428it("restarts for gateway.auth.token changes", () => {
415429const plan = buildGatewayReloadPlan(["gateway.auth.token"]);
416430expect(plan.restartGateway).toBe(true);
@@ -464,6 +478,11 @@ describe("buildGatewayReloadPlan", () => {
464478expectRestartGateway: false,
465479expectNoopPath: "gateway.remote.url",
466480},
481+{
482+path: "auth.cooldowns.billingBackoffHours",
483+expectRestartGateway: false,
484+expectHotPath: "auth.cooldowns.billingBackoffHours",
485+},
467486{
468487path: "gateway.auth.token",
469488expectRestartGateway: true,
@@ -1334,6 +1353,57 @@ describe("startGatewayConfigReloader", () => {
13341353await harness.reloader.stop();
13351354});
133613551356+it("keeps external auth cooldown writes on the hot reload path", async () => {
1357+const previousConfig: OpenClawConfig = {
1358+gateway: { reload: { debounceMs: 0 } },
1359+auth: {
1360+cooldowns: {
1361+billingBackoffHours: 1,
1362+billingBackoffHoursByProvider: { anthropic: 2 },
1363+},
1364+},
1365+};
1366+const nextConfig: OpenClawConfig = {
1367+gateway: { reload: { debounceMs: 0 } },
1368+auth: {
1369+cooldowns: {
1370+billingBackoffHours: 3,
1371+billingBackoffHoursByProvider: { anthropic: 4 },
1372+},
1373+},
1374+};
1375+const readSnapshot = vi.fn<() => Promise<ConfigFileSnapshot>>().mockResolvedValueOnce(
1376+makeSnapshot({
1377+sourceConfig: nextConfig,
1378+runtimeConfig: nextConfig,
1379+config: nextConfig,
1380+hash: "external-auth-cooldowns-1",
1381+}),
1382+);
1383+const harness = createReloaderHarness(readSnapshot, {
1384+initialCompareConfig: previousConfig,
1385+});
1386+1387+harness.watcher.emit("change");
1388+await vi.runOnlyPendingTimersAsync();
1389+1390+expect(harness.onRestart).not.toHaveBeenCalled();
1391+const [plan, hotConfig] = getOnlyHotReloadCall(harness);
1392+expect(plan.changedPaths).toEqual([
1393+"auth.cooldowns.billingBackoffHours",
1394+"auth.cooldowns.billingBackoffHoursByProvider.anthropic",
1395+]);
1396+expect(plan.restartGateway).toBe(false);
1397+expect(plan.hotReasons).toEqual([
1398+"auth.cooldowns.billingBackoffHours",
1399+"auth.cooldowns.billingBackoffHoursByProvider.anthropic",
1400+]);
1401+expect(plan.noopPaths).toStrictEqual([]);
1402+expect(hotConfig).toBe(nextConfig);
1403+1404+await harness.reloader.stop();
1405+});
1406+13371407it("queues restart when an external plugin source write also changes plugin config", async () => {
13381408const previousConfig: OpenClawConfig = {
13391409gateway: { reload: { debounceMs: 0 } },
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。