mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 21:48:27 +00:00
fix(sessions): add fix-missing cleanup path for orphaned store entries
Introduce a sessions cleanup flag to prune entries whose transcript files are missing and surface the exact remediation command from doctor to resolve missing-transcript deadlocks.
Made-with: Cursor
(cherry picked from commit 690d3d596b)
This commit is contained in:
committed by
Peter Steinberger
parent
a481ed00f5
commit
71e45ceecc
@@ -171,6 +171,7 @@ describe("registerStatusHealthSessionsCommands", () => {
|
|||||||
"/tmp/sessions.json",
|
"/tmp/sessions.json",
|
||||||
"--dry-run",
|
"--dry-run",
|
||||||
"--enforce",
|
"--enforce",
|
||||||
|
"--fix-missing",
|
||||||
"--active-key",
|
"--active-key",
|
||||||
"agent:main:main",
|
"agent:main:main",
|
||||||
"--json",
|
"--json",
|
||||||
@@ -183,6 +184,7 @@ describe("registerStatusHealthSessionsCommands", () => {
|
|||||||
allAgents: false,
|
allAgents: false,
|
||||||
dryRun: true,
|
dryRun: true,
|
||||||
enforce: true,
|
enforce: true,
|
||||||
|
fixMissing: true,
|
||||||
activeKey: "agent:main:main",
|
activeKey: "agent:main:main",
|
||||||
json: true,
|
json: true,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -163,6 +163,11 @@ export function registerStatusHealthSessionsCommands(program: Command) {
|
|||||||
.option("--all-agents", "Run maintenance across all configured agents", false)
|
.option("--all-agents", "Run maintenance across all configured agents", false)
|
||||||
.option("--dry-run", "Preview maintenance actions without writing", false)
|
.option("--dry-run", "Preview maintenance actions without writing", false)
|
||||||
.option("--enforce", "Apply maintenance even when configured mode is warn", false)
|
.option("--enforce", "Apply maintenance even when configured mode is warn", false)
|
||||||
|
.option(
|
||||||
|
"--fix-missing",
|
||||||
|
"Remove store entries whose transcript files are missing (bypasses age/count retention)",
|
||||||
|
false,
|
||||||
|
)
|
||||||
.option("--active-key <key>", "Protect this session key from budget-eviction")
|
.option("--active-key <key>", "Protect this session key from budget-eviction")
|
||||||
.option("--json", "Output JSON", false)
|
.option("--json", "Output JSON", false)
|
||||||
.addHelpText(
|
.addHelpText(
|
||||||
@@ -170,6 +175,10 @@ export function registerStatusHealthSessionsCommands(program: Command) {
|
|||||||
() =>
|
() =>
|
||||||
`\n${theme.heading("Examples:")}\n${formatHelpExamples([
|
`\n${theme.heading("Examples:")}\n${formatHelpExamples([
|
||||||
["openclaw sessions cleanup --dry-run", "Preview stale/cap cleanup."],
|
["openclaw sessions cleanup --dry-run", "Preview stale/cap cleanup."],
|
||||||
|
[
|
||||||
|
"openclaw sessions cleanup --dry-run --fix-missing",
|
||||||
|
"Also preview pruning entries with missing transcript files.",
|
||||||
|
],
|
||||||
["openclaw sessions cleanup --enforce", "Apply maintenance now."],
|
["openclaw sessions cleanup --enforce", "Apply maintenance now."],
|
||||||
["openclaw sessions cleanup --agent work --dry-run", "Preview one agent store."],
|
["openclaw sessions cleanup --agent work --dry-run", "Preview one agent store."],
|
||||||
["openclaw sessions cleanup --all-agents --dry-run", "Preview all agent stores."],
|
["openclaw sessions cleanup --all-agents --dry-run", "Preview all agent stores."],
|
||||||
@@ -196,6 +205,7 @@ export function registerStatusHealthSessionsCommands(program: Command) {
|
|||||||
allAgents: Boolean(opts.allAgents || parentOpts?.allAgents),
|
allAgents: Boolean(opts.allAgents || parentOpts?.allAgents),
|
||||||
dryRun: Boolean(opts.dryRun),
|
dryRun: Boolean(opts.dryRun),
|
||||||
enforce: Boolean(opts.enforce),
|
enforce: Boolean(opts.enforce),
|
||||||
|
fixMissing: Boolean(opts.fixMissing),
|
||||||
activeKey: opts.activeKey as string | undefined,
|
activeKey: opts.activeKey as string | undefined,
|
||||||
json: Boolean(opts.json || parentOpts?.json),
|
json: Boolean(opts.json || parentOpts?.json),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -168,6 +168,9 @@ describe("doctor state integrity oauth dir checks", () => {
|
|||||||
expect(text).toContain("recent sessions are missing transcripts");
|
expect(text).toContain("recent sessions are missing transcripts");
|
||||||
expect(text).toMatch(/openclaw sessions --store ".*sessions\.json"/);
|
expect(text).toMatch(/openclaw sessions --store ".*sessions\.json"/);
|
||||||
expect(text).toMatch(/openclaw sessions cleanup --store ".*sessions\.json" --dry-run/);
|
expect(text).toMatch(/openclaw sessions cleanup --store ".*sessions\.json" --dry-run/);
|
||||||
|
expect(text).toMatch(
|
||||||
|
/openclaw sessions cleanup --store ".*sessions\.json" --enforce --fix-missing/,
|
||||||
|
);
|
||||||
expect(text).not.toContain("--active");
|
expect(text).not.toContain("--active");
|
||||||
expect(text).not.toContain(" ls ");
|
expect(text).not.toContain(" ls ");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -438,6 +438,7 @@ export async function noteStateIntegrity(
|
|||||||
`- ${missing.length}/${recentTranscriptCandidates.length} recent sessions are missing transcripts.`,
|
`- ${missing.length}/${recentTranscriptCandidates.length} recent sessions are missing transcripts.`,
|
||||||
` Verify sessions in store: ${formatCliCommand(`openclaw sessions --store "${absoluteStorePath}"`)}`,
|
` Verify sessions in store: ${formatCliCommand(`openclaw sessions --store "${absoluteStorePath}"`)}`,
|
||||||
` Preview cleanup impact: ${formatCliCommand(`openclaw sessions cleanup --store "${absoluteStorePath}" --dry-run`)}`,
|
` Preview cleanup impact: ${formatCliCommand(`openclaw sessions cleanup --store "${absoluteStorePath}" --dry-run`)}`,
|
||||||
|
` Prune missing entries: ${formatCliCommand(`openclaw sessions cleanup --store "${absoluteStorePath}" --enforce --fix-missing`)}`,
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ const mocks = vi.hoisted(() => ({
|
|||||||
resolveSessionStoreTargets: vi.fn(),
|
resolveSessionStoreTargets: vi.fn(),
|
||||||
resolveMaintenanceConfig: vi.fn(),
|
resolveMaintenanceConfig: vi.fn(),
|
||||||
loadSessionStore: vi.fn(),
|
loadSessionStore: vi.fn(),
|
||||||
|
resolveSessionFilePath: vi.fn(),
|
||||||
|
resolveSessionFilePathOptions: vi.fn(),
|
||||||
pruneStaleEntries: vi.fn(),
|
pruneStaleEntries: vi.fn(),
|
||||||
capEntryCount: vi.fn(),
|
capEntryCount: vi.fn(),
|
||||||
updateSessionStore: vi.fn(),
|
updateSessionStore: vi.fn(),
|
||||||
@@ -24,6 +26,8 @@ vi.mock("./session-store-targets.js", () => ({
|
|||||||
vi.mock("../config/sessions.js", () => ({
|
vi.mock("../config/sessions.js", () => ({
|
||||||
resolveMaintenanceConfig: mocks.resolveMaintenanceConfig,
|
resolveMaintenanceConfig: mocks.resolveMaintenanceConfig,
|
||||||
loadSessionStore: mocks.loadSessionStore,
|
loadSessionStore: mocks.loadSessionStore,
|
||||||
|
resolveSessionFilePath: mocks.resolveSessionFilePath,
|
||||||
|
resolveSessionFilePathOptions: mocks.resolveSessionFilePathOptions,
|
||||||
pruneStaleEntries: mocks.pruneStaleEntries,
|
pruneStaleEntries: mocks.pruneStaleEntries,
|
||||||
capEntryCount: mocks.capEntryCount,
|
capEntryCount: mocks.capEntryCount,
|
||||||
updateSessionStore: mocks.updateSessionStore,
|
updateSessionStore: mocks.updateSessionStore,
|
||||||
@@ -74,8 +78,12 @@ describe("sessionsCleanupCommand", () => {
|
|||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
mocks.resolveSessionFilePathOptions.mockReturnValue({});
|
||||||
|
mocks.resolveSessionFilePath.mockImplementation(
|
||||||
|
(sessionId: string) => `/missing/${sessionId}.jsonl`,
|
||||||
|
);
|
||||||
mocks.capEntryCount.mockImplementation(() => 0);
|
mocks.capEntryCount.mockImplementation(() => 0);
|
||||||
mocks.updateSessionStore.mockResolvedValue(undefined);
|
mocks.updateSessionStore.mockResolvedValue(0);
|
||||||
mocks.enforceSessionDiskBudget.mockResolvedValue({
|
mocks.enforceSessionDiskBudget.mockResolvedValue({
|
||||||
totalBytesBefore: 1000,
|
totalBytesBefore: 1000,
|
||||||
totalBytesAfter: 700,
|
totalBytesAfter: 700,
|
||||||
@@ -130,6 +138,7 @@ describe("sessionsCleanupCommand", () => {
|
|||||||
overBudget: true,
|
overBudget: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
return 0;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -196,6 +205,29 @@ describe("sessionsCleanupCommand", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("counts missing transcript entries when --fix-missing is enabled in dry-run", async () => {
|
||||||
|
mocks.enforceSessionDiskBudget.mockResolvedValue(null);
|
||||||
|
mocks.loadSessionStore.mockReturnValue({
|
||||||
|
missing: { sessionId: "missing-transcript", updatedAt: 1 },
|
||||||
|
});
|
||||||
|
|
||||||
|
const { runtime, logs } = makeRuntime();
|
||||||
|
await sessionsCleanupCommand(
|
||||||
|
{
|
||||||
|
json: true,
|
||||||
|
dryRun: true,
|
||||||
|
fixMissing: true,
|
||||||
|
},
|
||||||
|
runtime,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(logs).toHaveLength(1);
|
||||||
|
const payload = JSON.parse(logs[0] ?? "{}") as Record<string, unknown>;
|
||||||
|
expect(payload.beforeCount).toBe(1);
|
||||||
|
expect(payload.afterCount).toBe(0);
|
||||||
|
expect(payload.missing).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
it("renders a dry-run action table with keep/prune actions", async () => {
|
it("renders a dry-run action table with keep/prune actions", async () => {
|
||||||
mocks.enforceSessionDiskBudget.mockResolvedValue(null);
|
mocks.enforceSessionDiskBudget.mockResolvedValue(null);
|
||||||
mocks.loadSessionStore.mockReturnValue({
|
mocks.loadSessionStore.mockReturnValue({
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
|
import fs from "node:fs";
|
||||||
import { loadConfig } from "../config/config.js";
|
import { loadConfig } from "../config/config.js";
|
||||||
import {
|
import {
|
||||||
capEntryCount,
|
capEntryCount,
|
||||||
enforceSessionDiskBudget,
|
enforceSessionDiskBudget,
|
||||||
|
resolveSessionFilePath,
|
||||||
|
resolveSessionFilePathOptions,
|
||||||
loadSessionStore,
|
loadSessionStore,
|
||||||
pruneStaleEntries,
|
pruneStaleEntries,
|
||||||
resolveMaintenanceConfig,
|
resolveMaintenanceConfig,
|
||||||
@@ -33,9 +36,15 @@ export type SessionsCleanupOptions = {
|
|||||||
enforce?: boolean;
|
enforce?: boolean;
|
||||||
activeKey?: string;
|
activeKey?: string;
|
||||||
json?: boolean;
|
json?: boolean;
|
||||||
|
fixMissing?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SessionCleanupAction = "keep" | "prune-stale" | "cap-overflow" | "evict-budget";
|
type SessionCleanupAction =
|
||||||
|
| "keep"
|
||||||
|
| "prune-missing"
|
||||||
|
| "prune-stale"
|
||||||
|
| "cap-overflow"
|
||||||
|
| "evict-budget";
|
||||||
|
|
||||||
const ACTION_PAD = 12;
|
const ACTION_PAD = 12;
|
||||||
|
|
||||||
@@ -50,6 +59,7 @@ type SessionCleanupSummary = {
|
|||||||
dryRun: boolean;
|
dryRun: boolean;
|
||||||
beforeCount: number;
|
beforeCount: number;
|
||||||
afterCount: number;
|
afterCount: number;
|
||||||
|
missing: number;
|
||||||
pruned: number;
|
pruned: number;
|
||||||
capped: number;
|
capped: number;
|
||||||
diskBudget: Awaited<ReturnType<typeof enforceSessionDiskBudget>>;
|
diskBudget: Awaited<ReturnType<typeof enforceSessionDiskBudget>>;
|
||||||
@@ -60,10 +70,14 @@ type SessionCleanupSummary = {
|
|||||||
|
|
||||||
function resolveSessionCleanupAction(params: {
|
function resolveSessionCleanupAction(params: {
|
||||||
key: string;
|
key: string;
|
||||||
|
missingKeys: Set<string>;
|
||||||
staleKeys: Set<string>;
|
staleKeys: Set<string>;
|
||||||
cappedKeys: Set<string>;
|
cappedKeys: Set<string>;
|
||||||
budgetEvictedKeys: Set<string>;
|
budgetEvictedKeys: Set<string>;
|
||||||
}): SessionCleanupAction {
|
}): SessionCleanupAction {
|
||||||
|
if (params.missingKeys.has(params.key)) {
|
||||||
|
return "prune-missing";
|
||||||
|
}
|
||||||
if (params.staleKeys.has(params.key)) {
|
if (params.staleKeys.has(params.key)) {
|
||||||
return "prune-stale";
|
return "prune-stale";
|
||||||
}
|
}
|
||||||
@@ -84,6 +98,9 @@ function formatCleanupActionCell(action: SessionCleanupAction, rich: boolean): s
|
|||||||
if (action === "keep") {
|
if (action === "keep") {
|
||||||
return theme.muted(label);
|
return theme.muted(label);
|
||||||
}
|
}
|
||||||
|
if (action === "prune-missing") {
|
||||||
|
return theme.error(label);
|
||||||
|
}
|
||||||
if (action === "prune-stale") {
|
if (action === "prune-stale") {
|
||||||
return theme.warn(label);
|
return theme.warn(label);
|
||||||
}
|
}
|
||||||
@@ -95,6 +112,7 @@ function formatCleanupActionCell(action: SessionCleanupAction, rich: boolean): s
|
|||||||
|
|
||||||
function buildActionRows(params: {
|
function buildActionRows(params: {
|
||||||
beforeStore: Record<string, SessionEntry>;
|
beforeStore: Record<string, SessionEntry>;
|
||||||
|
missingKeys: Set<string>;
|
||||||
staleKeys: Set<string>;
|
staleKeys: Set<string>;
|
||||||
cappedKeys: Set<string>;
|
cappedKeys: Set<string>;
|
||||||
budgetEvictedKeys: Set<string>;
|
budgetEvictedKeys: Set<string>;
|
||||||
@@ -103,6 +121,7 @@ function buildActionRows(params: {
|
|||||||
...row,
|
...row,
|
||||||
action: resolveSessionCleanupAction({
|
action: resolveSessionCleanupAction({
|
||||||
key: row.key,
|
key: row.key,
|
||||||
|
missingKeys: params.missingKeys,
|
||||||
staleKeys: params.staleKeys,
|
staleKeys: params.staleKeys,
|
||||||
cappedKeys: params.cappedKeys,
|
cappedKeys: params.cappedKeys,
|
||||||
budgetEvictedKeys: params.budgetEvictedKeys,
|
budgetEvictedKeys: params.budgetEvictedKeys,
|
||||||
@@ -110,17 +129,52 @@ function buildActionRows(params: {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pruneMissingTranscriptEntries(params: {
|
||||||
|
store: Record<string, SessionEntry>;
|
||||||
|
storePath: string;
|
||||||
|
onPruned?: (key: string) => void;
|
||||||
|
}): number {
|
||||||
|
const sessionPathOpts = resolveSessionFilePathOptions({
|
||||||
|
storePath: params.storePath,
|
||||||
|
});
|
||||||
|
let removed = 0;
|
||||||
|
for (const [key, entry] of Object.entries(params.store)) {
|
||||||
|
if (!entry?.sessionId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const transcriptPath = resolveSessionFilePath(entry.sessionId, entry, sessionPathOpts);
|
||||||
|
if (!fs.existsSync(transcriptPath)) {
|
||||||
|
delete params.store[key];
|
||||||
|
removed += 1;
|
||||||
|
params.onPruned?.(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
async function previewStoreCleanup(params: {
|
async function previewStoreCleanup(params: {
|
||||||
target: SessionStoreTarget;
|
target: SessionStoreTarget;
|
||||||
mode: "warn" | "enforce";
|
mode: "warn" | "enforce";
|
||||||
dryRun: boolean;
|
dryRun: boolean;
|
||||||
activeKey?: string;
|
activeKey?: string;
|
||||||
|
fixMissing?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const maintenance = resolveMaintenanceConfig();
|
const maintenance = resolveMaintenanceConfig();
|
||||||
const beforeStore = loadSessionStore(params.target.storePath, { skipCache: true });
|
const beforeStore = loadSessionStore(params.target.storePath, { skipCache: true });
|
||||||
const previewStore = structuredClone(beforeStore);
|
const previewStore = structuredClone(beforeStore);
|
||||||
const staleKeys = new Set<string>();
|
const staleKeys = new Set<string>();
|
||||||
const cappedKeys = new Set<string>();
|
const cappedKeys = new Set<string>();
|
||||||
|
const missingKeys = new Set<string>();
|
||||||
|
const missing =
|
||||||
|
params.fixMissing === true
|
||||||
|
? pruneMissingTranscriptEntries({
|
||||||
|
store: previewStore,
|
||||||
|
storePath: params.target.storePath,
|
||||||
|
onPruned: (key) => {
|
||||||
|
missingKeys.add(key);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
: 0;
|
||||||
const pruned = pruneStaleEntries(previewStore, maintenance.pruneAfterMs, {
|
const pruned = pruneStaleEntries(previewStore, maintenance.pruneAfterMs, {
|
||||||
log: false,
|
log: false,
|
||||||
onPruned: ({ key }) => {
|
onPruned: ({ key }) => {
|
||||||
@@ -151,6 +205,7 @@ async function previewStoreCleanup(params: {
|
|||||||
const beforeCount = Object.keys(beforeStore).length;
|
const beforeCount = Object.keys(beforeStore).length;
|
||||||
const afterPreviewCount = Object.keys(previewStore).length;
|
const afterPreviewCount = Object.keys(previewStore).length;
|
||||||
const wouldMutate =
|
const wouldMutate =
|
||||||
|
missing > 0 ||
|
||||||
pruned > 0 ||
|
pruned > 0 ||
|
||||||
capped > 0 ||
|
capped > 0 ||
|
||||||
Boolean((diskBudget?.removedEntries ?? 0) > 0 || (diskBudget?.removedFiles ?? 0) > 0);
|
Boolean((diskBudget?.removedEntries ?? 0) > 0 || (diskBudget?.removedFiles ?? 0) > 0);
|
||||||
@@ -162,6 +217,7 @@ async function previewStoreCleanup(params: {
|
|||||||
dryRun: params.dryRun,
|
dryRun: params.dryRun,
|
||||||
beforeCount,
|
beforeCount,
|
||||||
afterCount: afterPreviewCount,
|
afterCount: afterPreviewCount,
|
||||||
|
missing,
|
||||||
pruned,
|
pruned,
|
||||||
capped,
|
capped,
|
||||||
diskBudget,
|
diskBudget,
|
||||||
@@ -175,6 +231,7 @@ async function previewStoreCleanup(params: {
|
|||||||
staleKeys,
|
staleKeys,
|
||||||
cappedKeys,
|
cappedKeys,
|
||||||
budgetEvictedKeys,
|
budgetEvictedKeys,
|
||||||
|
missingKeys,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -196,6 +253,7 @@ function renderStoreDryRunPlan(params: {
|
|||||||
params.runtime.log(
|
params.runtime.log(
|
||||||
`Entries: ${params.summary.beforeCount} -> ${params.summary.afterCount} (remove ${params.summary.beforeCount - params.summary.afterCount})`,
|
`Entries: ${params.summary.beforeCount} -> ${params.summary.afterCount} (remove ${params.summary.beforeCount - params.summary.afterCount})`,
|
||||||
);
|
);
|
||||||
|
params.runtime.log(`Would prune missing transcripts: ${params.summary.missing}`);
|
||||||
params.runtime.log(`Would prune stale: ${params.summary.pruned}`);
|
params.runtime.log(`Would prune stale: ${params.summary.pruned}`);
|
||||||
params.runtime.log(`Would cap overflow: ${params.summary.capped}`);
|
params.runtime.log(`Would cap overflow: ${params.summary.capped}`);
|
||||||
if (params.summary.diskBudget) {
|
if (params.summary.diskBudget) {
|
||||||
@@ -256,6 +314,7 @@ export async function sessionsCleanupCommand(opts: SessionsCleanupOptions, runti
|
|||||||
mode,
|
mode,
|
||||||
dryRun: Boolean(opts.dryRun),
|
dryRun: Boolean(opts.dryRun),
|
||||||
activeKey: opts.activeKey,
|
activeKey: opts.activeKey,
|
||||||
|
fixMissing: Boolean(opts.fixMissing),
|
||||||
});
|
});
|
||||||
previewResults.push(result);
|
previewResults.push(result);
|
||||||
}
|
}
|
||||||
@@ -303,10 +362,16 @@ export async function sessionsCleanupCommand(opts: SessionsCleanupOptions, runti
|
|||||||
const appliedReportRef: { current: SessionMaintenanceApplyReport | null } = {
|
const appliedReportRef: { current: SessionMaintenanceApplyReport | null } = {
|
||||||
current: null,
|
current: null,
|
||||||
};
|
};
|
||||||
await updateSessionStore(
|
const missingApplied = await updateSessionStore(
|
||||||
target.storePath,
|
target.storePath,
|
||||||
async () => {
|
async (store) => {
|
||||||
// Maintenance runs in saveSessionStoreUnlocked(); no direct store mutation needed here.
|
if (!opts.fixMissing) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return pruneMissingTranscriptEntries({
|
||||||
|
store,
|
||||||
|
storePath: target.storePath,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
activeSessionKey: opts.activeKey,
|
activeSessionKey: opts.activeKey,
|
||||||
@@ -331,6 +396,7 @@ export async function sessionsCleanupCommand(opts: SessionsCleanupOptions, runti
|
|||||||
dryRun: false,
|
dryRun: false,
|
||||||
beforeCount: 0,
|
beforeCount: 0,
|
||||||
afterCount: 0,
|
afterCount: 0,
|
||||||
|
missing: 0,
|
||||||
pruned: 0,
|
pruned: 0,
|
||||||
capped: 0,
|
capped: 0,
|
||||||
diskBudget: null,
|
diskBudget: null,
|
||||||
@@ -347,10 +413,12 @@ export async function sessionsCleanupCommand(opts: SessionsCleanupOptions, runti
|
|||||||
dryRun: false,
|
dryRun: false,
|
||||||
beforeCount: appliedReport.beforeCount,
|
beforeCount: appliedReport.beforeCount,
|
||||||
afterCount: appliedReport.afterCount,
|
afterCount: appliedReport.afterCount,
|
||||||
|
missing: missingApplied,
|
||||||
pruned: appliedReport.pruned,
|
pruned: appliedReport.pruned,
|
||||||
capped: appliedReport.capped,
|
capped: appliedReport.capped,
|
||||||
diskBudget: appliedReport.diskBudget,
|
diskBudget: appliedReport.diskBudget,
|
||||||
wouldMutate:
|
wouldMutate:
|
||||||
|
missingApplied > 0 ||
|
||||||
appliedReport.pruned > 0 ||
|
appliedReport.pruned > 0 ||
|
||||||
appliedReport.capped > 0 ||
|
appliedReport.capped > 0 ||
|
||||||
Boolean(
|
Boolean(
|
||||||
|
|||||||
Reference in New Issue
Block a user