From 3ce0e80f57792a9bd75638f275e1b011262c3e78 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 15 Feb 2026 17:09:12 +0000 Subject: [PATCH] refactor(commands): dedupe cleanup path resolution --- src/commands/cleanup-utils.test.ts | 26 ++++++++++++++++++++++++++ src/commands/cleanup-utils.ts | 17 +++++++++++++++++ src/commands/reset.ts | 16 +++++++--------- src/commands/uninstall.ts | 11 +++++++---- 4 files changed, 57 insertions(+), 13 deletions(-) create mode 100644 src/commands/cleanup-utils.test.ts diff --git a/src/commands/cleanup-utils.test.ts b/src/commands/cleanup-utils.test.ts new file mode 100644 index 00000000000..1df18875eb1 --- /dev/null +++ b/src/commands/cleanup-utils.test.ts @@ -0,0 +1,26 @@ +import { describe, expect, test } from "vitest"; +import type { OpenClawConfig } from "../config/config.js"; +import { buildCleanupPlan } from "./cleanup-utils.js"; + +describe("buildCleanupPlan", () => { + test("resolves inside-state flags and workspace dirs", () => { + const cfg = { + agents: { + defaults: { workspace: "/tmp/openclaw-workspace-1" }, + list: [{ workspace: "/tmp/openclaw-workspace-2" }], + }, + }; + const plan = buildCleanupPlan({ + cfg: cfg as unknown as OpenClawConfig, + stateDir: "/tmp/openclaw-state", + configPath: "/tmp/openclaw-state/openclaw.json", + oauthDir: "/tmp/openclaw-oauth", + }); + + expect(plan.configInsideState).toBe(true); + expect(plan.oauthInsideState).toBe(false); + expect(new Set(plan.workspaceDirs)).toEqual( + new Set(["/tmp/openclaw-workspace-1", "/tmp/openclaw-workspace-2"]), + ); + }); +}); diff --git a/src/commands/cleanup-utils.ts b/src/commands/cleanup-utils.ts index a1c8dbb7c50..edca7757daa 100644 --- a/src/commands/cleanup-utils.ts +++ b/src/commands/cleanup-utils.ts @@ -29,6 +29,23 @@ export function collectWorkspaceDirs(cfg: OpenClawConfig | undefined): string[] return [...dirs]; } +export function buildCleanupPlan(params: { + cfg: OpenClawConfig | undefined; + stateDir: string; + configPath: string; + oauthDir: string; +}): { + configInsideState: boolean; + oauthInsideState: boolean; + workspaceDirs: string[]; +} { + return { + configInsideState: isPathWithin(params.configPath, params.stateDir), + oauthInsideState: isPathWithin(params.oauthDir, params.stateDir), + workspaceDirs: collectWorkspaceDirs(params.cfg), + }; +} + export function isPathWithin(child: string, parent: string): boolean { const relative = path.relative(parent, child); return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative)); diff --git a/src/commands/reset.ts b/src/commands/reset.ts index 0717544a431..e2f791994e7 100644 --- a/src/commands/reset.ts +++ b/src/commands/reset.ts @@ -10,12 +10,7 @@ import { } from "../config/config.js"; import { resolveGatewayService } from "../daemon/service.js"; import { stylePromptHint, stylePromptMessage, stylePromptTitle } from "../terminal/prompt-style.js"; -import { - collectWorkspaceDirs, - isPathWithin, - listAgentSessionDirs, - removePath, -} from "./cleanup-utils.js"; +import { buildCleanupPlan, listAgentSessionDirs, removePath } from "./cleanup-utils.js"; export type ResetScope = "config" | "config+creds+sessions" | "full"; @@ -123,9 +118,12 @@ export async function resetCommand(runtime: RuntimeEnv, opts: ResetOptions) { const stateDir = resolveStateDir(); const configPath = resolveConfigPath(); const oauthDir = resolveOAuthDir(); - const configInsideState = isPathWithin(configPath, stateDir); - const oauthInsideState = isPathWithin(oauthDir, stateDir); - const workspaceDirs = collectWorkspaceDirs(cfg); + const { configInsideState, oauthInsideState, workspaceDirs } = buildCleanupPlan({ + cfg, + stateDir, + configPath, + oauthDir, + }); if (scope !== "config") { if (dryRun) { diff --git a/src/commands/uninstall.ts b/src/commands/uninstall.ts index 6f9e9941e3c..e0754514e8b 100644 --- a/src/commands/uninstall.ts +++ b/src/commands/uninstall.ts @@ -11,7 +11,7 @@ import { import { resolveGatewayService } from "../daemon/service.js"; import { stylePromptHint, stylePromptMessage, stylePromptTitle } from "../terminal/prompt-style.js"; import { resolveHomeDir } from "../utils.js"; -import { collectWorkspaceDirs, isPathWithin, removePath } from "./cleanup-utils.js"; +import { buildCleanupPlan, removePath } from "./cleanup-utils.js"; type UninstallScope = "service" | "state" | "workspace" | "app"; @@ -161,9 +161,12 @@ export async function uninstallCommand(runtime: RuntimeEnv, opts: UninstallOptio const stateDir = resolveStateDir(); const configPath = resolveConfigPath(); const oauthDir = resolveOAuthDir(); - const configInsideState = isPathWithin(configPath, stateDir); - const oauthInsideState = isPathWithin(oauthDir, stateDir); - const workspaceDirs = collectWorkspaceDirs(cfg); + const { configInsideState, oauthInsideState, workspaceDirs } = buildCleanupPlan({ + cfg, + stateDir, + configPath, + oauthDir, + }); if (scopes.has("service")) { if (dryRun) {