Files
kandev/apps/web/components/task/task-cleanup-summary.ts
T
2026-09-22 23:31:49 +01:00

148 lines
4.4 KiB
TypeScript

// Executor types match models.ExecutorType in apps/backend/internal/task/models/models.go.
import { t } from "@/lib/i18n";
import type { WorkspaceMode } from "@/lib/kanban/map-task";
export type CleanupSummary = {
effects: string[];
notes: string[];
};
export type CleanupSummaryOptions = {
sharesParentWorkspace?: boolean;
};
export function cleanupSharesParentWorkspace(
workspaceMode: WorkspaceMode | null | undefined,
): boolean | undefined {
return workspaceMode == null ? undefined : workspaceMode === "inherit_parent";
}
// mock_remote is test-only and intentionally falls through to the generic effect.
type KnownExecutor =
| "local"
| "worktree"
| "local_docker"
| "remote_docker"
| "sprites"
| "ssh"
| "k8s";
type CleanupCopy = {
effectKey?: string;
noteKey?: string;
};
/**
* Catalog keys, not copy. The values resolve inside each public function so a
* locale switch updates both the effect list and the supporting notes.
*/
const SINGLE_COPIES: Record<KnownExecutor, CleanupCopy> = {
local: { noteKey: "task:cleanupSingleLocal" },
worktree: {
effectKey: "task:cleanupSingleWorktree",
noteKey: "task:cleanupSingleWorktreeNote",
},
local_docker: {
effectKey: "task:cleanupSingleLocalDocker",
noteKey: "task:cleanupSingleLocalDockerNote",
},
remote_docker: { effectKey: "task:cleanupSingleRemoteDocker" },
sprites: { effectKey: "task:cleanupSingleSprites" },
ssh: {
effectKey: "task:cleanupSingleSsh",
noteKey: "task:cleanupSingleSshNote",
},
k8s: { effectKey: "task:cleanupSingleKubernetes" },
};
const GENERIC_EFFECT_KEY = "task:cleanupAgentSessionsStopped";
const INHERITED_PARENT_WORKSPACE_NOTE_KEY = "task:cleanupInheritedParentWorkspaceNote";
function normalize(executorType: string | null | undefined): KnownExecutor | null {
if (!executorType) return null;
const key = executorType.toLowerCase();
if (Object.hasOwn(SINGLE_COPIES, key)) return key as KnownExecutor;
return null;
}
export function hasWorktreeExecutor(executorType: string | null | undefined): boolean {
return normalize(executorType) === "worktree";
}
function resolveCopy(copy: CleanupCopy, options?: { count: number }): CleanupSummary {
const effects = copy.effectKey ? [t(copy.effectKey, options)] : [];
effects.push(t(GENERIC_EFFECT_KEY));
const notes = copy.noteKey ? [t(copy.noteKey, options)] : [];
return { effects, notes };
}
/** Single-task variant. */
export function getCleanupSummary(
executorType: string | null | undefined,
options?: CleanupSummaryOptions,
): CleanupSummary {
if (options?.sharesParentWorkspace) {
return {
effects: [t(GENERIC_EFFECT_KEY)],
notes: [t(INHERITED_PARENT_WORKSPACE_NOTE_KEY)],
};
}
const known = normalize(executorType);
return known
? resolveCopy(SINGLE_COPIES[known])
: { effects: [t(GENERIC_EFFECT_KEY)], notes: [] };
}
/** Catalog keys for the grouped variant. */
const BULK_COPIES: Record<KnownExecutor, CleanupCopy> = {
local: { noteKey: "task:cleanupBulkLocal" },
worktree: {
effectKey: "task:cleanupBulkWorktree",
noteKey: "task:cleanupBulkWorktreeNote",
},
local_docker: {
effectKey: "task:cleanupBulkLocalDocker",
noteKey: "task:cleanupBulkLocalDockerNote",
},
remote_docker: { effectKey: "task:cleanupBulkRemoteDocker" },
sprites: { effectKey: "task:cleanupBulkSprites" },
ssh: {
effectKey: "task:cleanupBulkSsh",
noteKey: "task:cleanupBulkSshNote",
},
k8s: { effectKey: "task:cleanupBulkKubernetes" },
};
/** Bulk variant. Groups known tasks by executor type and preserves display order. */
export function getBulkCleanupSummary(
executorTypes: Array<string | null | undefined>,
): CleanupSummary {
const counts = new Map<KnownExecutor, number>();
for (const executorType of executorTypes) {
const known = normalize(executorType);
if (!known) continue;
counts.set(known, (counts.get(known) ?? 0) + 1);
}
const order: KnownExecutor[] = [
"worktree",
"local_docker",
"remote_docker",
"sprites",
"ssh",
"k8s",
"local",
];
const effects: string[] = [];
const notes: string[] = [];
for (const key of order) {
const count = counts.get(key);
if (!count) continue;
const copy = BULK_COPIES[key];
if (copy.effectKey) effects.push(t(copy.effectKey, { count }));
if (copy.noteKey) notes.push(t(copy.noteKey, { count }));
}
effects.push(t(GENERIC_EFFECT_KEY));
return { effects, notes };
}