diff --git a/src/commands/onboard.test.ts b/src/commands/onboard.test.ts index 07adc2505a23..5ee8ec3def69 100644 --- a/src/commands/onboard.test.ts +++ b/src/commands/onboard.test.ts @@ -5,6 +5,7 @@ import { formatCliCommand } from "../cli/command-format.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import type { ProviderAuthMethod, ProviderPlugin } from "../plugins/types.js"; import type { RuntimeEnv } from "../runtime.js"; +import { resolveUserPath } from "../utils.js"; import { setupWizardCommand } from "./onboard.js"; type ConfigSnapshotStub = { @@ -12,6 +13,7 @@ type ConfigSnapshotStub = { valid: boolean; config: OpenClawConfig; sourceConfig?: OpenClawConfig; + readError?: { code: string | null }; }; type ProviderAuthMethodNonInteractiveValidationContext = Parameters< @@ -364,10 +366,14 @@ describe("setupWizardCommand", () => { it("requires an explicit workspace for a full reset when config is unreadable", async () => { const runtime = makeRuntime(); + // readConfigFileSnapshot always returns a sourceConfig object, so an + // unreadable config is only recognizable through readError. mocks.readConfigFileSnapshot.mockResolvedValue({ exists: true, valid: false, config: {}, + sourceConfig: {}, + readError: { code: "EACCES" }, }); await setupWizardCommand( @@ -384,6 +390,30 @@ describe("setupWizardCommand", () => { expect(mocks.handleReset).not.toHaveBeenCalled(); }); + it("uses the default workspace for a full reset when a readable config configures none", async () => { + const runtime = makeRuntime(); + mocks.readConfigFileSnapshot.mockResolvedValue({ + exists: true, + valid: false, + config: {}, + sourceConfig: { gateway: { port: 1 } }, + }); + + await setupWizardCommand( + { + reset: true, + resetScope: "full", + }, + runtime, + ); + + expect(mocks.handleReset).toHaveBeenCalledWith( + "full", + resolveUserPath("~/.openclaw/workspace"), + runtime, + ); + }); + it("accepts explicit --reset-scope full", async () => { const runtime = makeRuntime(); diff --git a/src/commands/onboard.ts b/src/commands/onboard.ts index 0c2755a00266..2e34e49d6ab2 100644 --- a/src/commands/onboard.ts +++ b/src/commands/onboard.ts @@ -578,7 +578,10 @@ export async function setupWizardCommand( normalizedOpts.workspace === undefined && snapshot.exists && !snapshot.valid && - !snapshot.sourceConfig + // A snapshot always carries a sourceConfig object (empty on failure), so + // only readError distinguishes "config could not be read" from "config + // parsed but configures no workspace", where the default is correct. + snapshot.readError !== undefined ) { rejectOption( runtime,