fix(server): harden worktree archive with parallel teardown

Fan out agent close + terminal kill concurrently with Promise.allSettled
so one failure never leaves the worktree half-dead. Await real PTY exit
with SIGHUP→SIGKILL escalation before rm to avoid EBUSY races. Tolerate
missing repoRoot and retry directory removal with backoff. Purge stale
client workspace layout/tab state after successful archive.
This commit is contained in:
Mohamed Boudra
2026-04-17 15:50:48 +07:00
parent ed2dc08443
commit d6585f7c3a
9 changed files with 537 additions and 65 deletions
@@ -93,6 +93,7 @@ type WorkspaceTabsState = {
}) => string | null;
reorderTabs: (input: { serverId: string; workspaceId: string; tabIds: string[] }) => void;
getWorkspaceTabs: (input: { serverId: string; workspaceId: string }) => WorkspaceTab[];
purgeWorkspace: (input: { serverId: string; workspaceId: string }) => void;
};
export const useWorkspaceTabsStore = create<WorkspaceTabsState>()(
@@ -326,6 +327,31 @@ export const useWorkspaceTabsStore = create<WorkspaceTabsState>()(
}
return get().uiTabsByWorkspace[key] ?? [];
},
purgeWorkspace: ({ serverId, workspaceId }) => {
const key = buildWorkspaceTabPersistenceKey({ serverId, workspaceId });
if (!key) {
return;
}
set((state) => {
if (
!(key in state.uiTabsByWorkspace) &&
!(key in state.tabOrderByWorkspace) &&
!(key in state.focusedTabIdByWorkspace)
) {
return state;
}
const { [key]: _tabs, ...remainingUiTabsByWorkspace } = state.uiTabsByWorkspace;
const { [key]: _order, ...remainingTabOrderByWorkspace } = state.tabOrderByWorkspace;
const { [key]: _focused, ...remainingFocusedTabIdByWorkspace } =
state.focusedTabIdByWorkspace;
return {
...state,
uiTabsByWorkspace: remainingUiTabsByWorkspace,
tabOrderByWorkspace: remainingTabOrderByWorkspace,
focusedTabIdByWorkspace: remainingFocusedTabIdByWorkspace,
};
});
},
}),
{
name: "workspace-tabs-state",