























@@ -9,7 +9,9 @@ const mocks = vi.hoisted(() => ({
99getInstalledPluginRecord: vi.fn(),
1010isInstalledPluginEnabled: vi.fn(),
1111loadInstalledPluginIndex: vi.fn(),
12+maybeRepairGroupAllowFromFallback: vi.fn(),
1213maybeRepairManagedNpmOpenClawPeerLinks: vi.fn(),
14+maybeRepairOpenPolicyAllowFrom: vi.fn(),
1315maybeRepairStaleManagedNpmBundledPlugins: vi.fn(),
1416maybeRepairStalePluginConfig: vi.fn(),
1517repairStaleOAuthProfileShadows: vi.fn(),
@@ -101,6 +103,10 @@ vi.mock("./shared/allowlist-policy-repair.js", () => ({
101103}),
102104}));
103105106+vi.mock("./shared/allowfrom-fallback-migration.js", () => ({
107+maybeRepairGroupAllowFromFallback: mocks.maybeRepairGroupAllowFromFallback,
108+}));
109+104110vi.mock("./shared/bundled-plugin-load-paths.js", () => ({
105111maybeRepairBundledPluginLoadPaths: (cfg: OpenClawConfig) => ({
106112config: cfg,
@@ -109,10 +115,7 @@ vi.mock("./shared/bundled-plugin-load-paths.js", () => ({
109115}));
110116111117vi.mock("./shared/open-policy-allowfrom.js", () => ({
112-maybeRepairOpenPolicyAllowFrom: (cfg: OpenClawConfig) => ({
113-config: cfg,
114-changes: [],
115-}),
118+maybeRepairOpenPolicyAllowFrom: mocks.maybeRepairOpenPolicyAllowFrom,
116119}));
117120118121vi.mock("./shared/stale-plugin-config.js", () => ({
@@ -174,6 +177,13 @@ vi.mock("./shared/exec-safe-bins.js", () => ({
174177}),
175178}));
176179180+vi.mock("./shared/plugin-dependency-cleanup.js", () => ({
181+cleanupLegacyPluginDependencyState: async () => ({
182+changes: [],
183+warnings: [],
184+}),
185+}));
186+177187describe("doctor repair sequencing", () => {
178188beforeEach(() => {
179189vi.clearAllMocks();
@@ -192,7 +202,15 @@ describe("doctor repair sequencing", () => {
192202mocks.getInstalledPluginRecord.mockReturnValue(undefined);
193203mocks.isInstalledPluginEnabled.mockReturnValue(false);
194204mocks.loadInstalledPluginIndex.mockReturnValue({ plugins: [] });
205+mocks.maybeRepairGroupAllowFromFallback.mockImplementation((cfg: OpenClawConfig) => ({
206+config: cfg,
207+changes: [],
208+}));
195209mocks.maybeRepairManagedNpmOpenClawPeerLinks.mockResolvedValue(false);
210+mocks.maybeRepairOpenPolicyAllowFrom.mockImplementation((cfg: OpenClawConfig) => ({
211+config: cfg,
212+changes: [],
213+}));
196214mocks.maybeRepairStaleManagedNpmBundledPlugins.mockReturnValue(false);
197215mocks.repairMissingConfiguredPluginInstalls.mockResolvedValue({
198216changes: [],
@@ -444,6 +462,72 @@ describe("doctor repair sequencing", () => {
444462]);
445463});
446464465+it("runs group allowFrom fallback migration after open-policy allowFrom repair", async () => {
466+const events: string[] = [];
467+mocks.maybeRepairOpenPolicyAllowFrom.mockImplementationOnce((cfg: OpenClawConfig) => {
468+events.push("open-policy");
469+return {
470+config: {
471+ ...cfg,
472+channels: {
473+ ...cfg.channels,
474+signal: {
475+ ...cfg.channels?.signal,
476+allowFrom: ["*"],
477+},
478+},
479+},
480+changes: ['channels.signal.allowFrom: set to ["*"]'],
481+};
482+});
483+mocks.maybeRepairGroupAllowFromFallback.mockImplementationOnce((cfg: OpenClawConfig) => {
484+events.push("group-fallback");
485+expect(cfg.channels?.signal?.allowFrom).toEqual(["*"]);
486+return {
487+config: {
488+ ...cfg,
489+channels: {
490+ ...cfg.channels,
491+signal: {
492+ ...cfg.channels?.signal,
493+groupAllowFrom: ["*"],
494+},
495+},
496+},
497+changes: ["channels.signal.groupAllowFrom: copied 1 sender entry from allowFrom"],
498+};
499+});
500+501+const result = await runDoctorRepairSequence({
502+state: {
503+cfg: {
504+channels: {
505+signal: {
506+dmPolicy: "open",
507+},
508+},
509+} as OpenClawConfig,
510+candidate: {
511+channels: {
512+signal: {
513+dmPolicy: "open",
514+},
515+},
516+} as OpenClawConfig,
517+pendingChanges: false,
518+fixHints: [],
519+},
520+doctorFixCommand: "openclaw doctor --fix",
521+});
522+523+expect(events).toEqual(["open-policy", "group-fallback"]);
524+expect(result.state.candidate.channels?.signal?.groupAllowFrom).toEqual(["*"]);
525+expect(result.changeNotes).toContain('channels.signal.allowFrom: set to ["*"]');
526+expect(result.changeNotes).toContain(
527+"channels.signal.groupAllowFrom: copied 1 sender entry from allowFrom",
528+);
529+});
530+447531it("does not remove deferred configured plugins during the package update doctor pass", async () => {
448532mocks.repairMissingConfiguredPluginInstalls.mockResolvedValueOnce({
449533changes: [
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。