feat: add CLI session sharing functionality

- Implemented share token creation and revocation for CLI sessions.
- Added a new page for viewing shared CLI sessions with SSE support.
- Introduced hooks for fetching and managing CLI session shares.
- Enhanced the IssueTerminalTab component to handle share tokens and display active shares.
- Updated API routes to support fetching and revoking share tokens.
- Added unit tests for the CLI session share manager and rate limiter.
- Updated localization files to include new strings for sharing functionality.
This commit is contained in:
catlog22
2026-02-09 22:57:05 +08:00
parent 362f354f1c
commit d0cdee2e68
18 changed files with 748 additions and 23 deletions

View File

@@ -69,6 +69,7 @@ export type {
export {
useIssues,
useIssueQueue,
useIssueQueueById,
useQueueHistory,
useCreateIssue,
useUpdateIssue,

View File

@@ -8,6 +8,7 @@ import {
fetchIssues,
fetchIssueHistory,
fetchIssueQueue,
fetchQueueById,
fetchQueueHistory,
createIssue,
updateIssue,
@@ -208,6 +209,23 @@ export function useIssueQueue(): UseQueryResult<IssueQueue> {
});
}
/**
* Hook for fetching a specific queue by ID
*/
export function useIssueQueueById(queueId?: string): UseQueryResult<IssueQueue> {
const projectPath = useWorkflowStore(selectProjectPath);
return useQuery<IssueQueue>({
queryKey:
projectPath && queueId
? workspaceQueryKeys.issueQueueById(projectPath, queueId)
: ['issueQueueById', projectPath ?? 'no-project', queueId ?? 'no-queue'],
queryFn: () => fetchQueueById(queueId!, projectPath),
staleTime: STALE_TIME,
enabled: !!projectPath && !!queueId,
retry: 2,
});
}
// ========== Mutations ==========
export interface UseCreateIssueReturn {
@@ -346,6 +364,7 @@ export function useQueueMutations(): UseQueueMutationsReturn {
mutationFn: (queueId: string) => activateQueue(queueId, projectPath),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: workspaceQueryKeys.issueQueue(projectPath) });
queryClient.invalidateQueries({ queryKey: [...workspaceQueryKeys.issues(projectPath), 'queueById'] });
queryClient.invalidateQueries({ queryKey: workspaceQueryKeys.issueQueueHistory(projectPath) });
},
});
@@ -354,6 +373,7 @@ export function useQueueMutations(): UseQueueMutationsReturn {
mutationFn: () => deactivateQueue(projectPath),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: workspaceQueryKeys.issueQueue(projectPath) });
queryClient.invalidateQueries({ queryKey: [...workspaceQueryKeys.issues(projectPath), 'queueById'] });
queryClient.invalidateQueries({ queryKey: workspaceQueryKeys.issueQueueHistory(projectPath) });
},
});
@@ -362,6 +382,7 @@ export function useQueueMutations(): UseQueueMutationsReturn {
mutationFn: (queueId: string) => deleteQueueApi(queueId, projectPath),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: workspaceQueryKeys.issueQueue(projectPath) });
queryClient.invalidateQueries({ queryKey: [...workspaceQueryKeys.issues(projectPath), 'queueById'] });
queryClient.invalidateQueries({ queryKey: workspaceQueryKeys.issueQueueHistory(projectPath) });
},
});
@@ -371,6 +392,7 @@ export function useQueueMutations(): UseQueueMutationsReturn {
mergeQueuesApi(sourceId, targetId, projectPath),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: workspaceQueryKeys.issueQueue(projectPath) });
queryClient.invalidateQueries({ queryKey: [...workspaceQueryKeys.issues(projectPath), 'queueById'] });
queryClient.invalidateQueries({ queryKey: workspaceQueryKeys.issueQueueHistory(projectPath) });
},
});
@@ -380,6 +402,7 @@ export function useQueueMutations(): UseQueueMutationsReturn {
splitQueueApi(sourceQueueId, itemIds, projectPath),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: workspaceQueryKeys.issueQueue(projectPath) });
queryClient.invalidateQueries({ queryKey: [...workspaceQueryKeys.issues(projectPath), 'queueById'] });
queryClient.invalidateQueries({ queryKey: workspaceQueryKeys.issueQueueHistory(projectPath) });
},
});
@@ -389,6 +412,7 @@ export function useQueueMutations(): UseQueueMutationsReturn {
reorderQueueGroupApi(projectPath, { groupId, newOrder }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: workspaceQueryKeys.issueQueue(projectPath) });
queryClient.invalidateQueries({ queryKey: [...workspaceQueryKeys.issues(projectPath), 'queueById'] });
},
});
@@ -397,6 +421,7 @@ export function useQueueMutations(): UseQueueMutationsReturn {
moveQueueItemApi(projectPath, { itemId, toGroupId, toIndex }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: workspaceQueryKeys.issueQueue(projectPath) });
queryClient.invalidateQueries({ queryKey: [...workspaceQueryKeys.issues(projectPath), 'queueById'] });
},
});