返回 DeepSeek-Reasonix
projectTopicOwner.ts
根目录 / desktop / frontend / src / app-runtime / projectTopicOwner.ts
1 import { CommandCancelled } from "../lib/commandOutcome";
2 import type { RemoteSessionView } from "../lib/remoteTypes";
3 import type { SessionOperationAuthority } from "./useResourceOperations";
4 import type { SessionSelector } from "../generated/desktopContract.generated";
5
6 export type TopicRenameTarget =
7 | { kind: "local"; topicId: string; selector: SessionSelector }
8 | { kind: "remote"; hostId: string; workspace: string; sessionPath: string; sessionId?: string };
9 export type ProjectTopicPorts = {
10 renameLocal: (selector: SessionSelector, title: string) => Promise<unknown>;
11 listRemote: (host: string, workspace: string) => Promise<RemoteSessionView[]>;
12 renameRemote: (host: string, workspace: string, name: string, title: string) => Promise<void>;
13 markChanged: (update: (value: number) => number) => void;
14 refreshTabs: (apply?: () => boolean, options?: { afterMutation?: boolean }) => Promise<readonly { id: string }[]>;
15 syncActive: (rebuild: boolean) => Promise<unknown>;
16 };
17 export type ProjectRefreshInput = { activeTabId?: string; ports: ProjectTopicPorts };
18
19 export async function refreshProjectTopics(input: ProjectRefreshInput, authority: SessionOperationAuthority) {
20 authority.checkpoint();
21 input.ports.markChanged(value => value + 1);
22 const tabs = await input.ports.refreshTabs(() => {
23 try { authority.checkpoint(); return true; } catch { return false; }
24 }, { afterMutation: true });
25 authority.checkpoint();
26 if (authority.ownsUI() && input.activeTabId && !tabs.some(tab => tab.id === input.activeTabId)) await input.ports.syncActive(false);
27 }
28
29 export async function renameProjectTopic(input: ProjectRefreshInput & { target: TopicRenameTarget; title: string }, authority: SessionOperationAuthority) {
30 const { target, title, ports } = input;
31 authority.checkpoint();
32 if (target.kind === "local") await ports.renameLocal(target.selector, title);
33 else {
34 const sessions = await ports.listRemote(target.hostId, target.workspace);
35 authority.checkpoint();
36 // `current` is a navigation snapshot, not the identity of the rename target.
37 const matches = sessions.filter(session => target.sessionId
38 ? session.sessionId === target.sessionId
39 : Boolean(target.sessionPath) && session.path === target.sessionPath);
40 if (matches.length === 0) throw new CommandCancelled("superseded");
41 const source = matches[0];
42 if (matches.length !== 1 || !source.name || sessions.filter(session => session.name === source.name).length !== 1) {
43 throw new Error("The remote service cannot uniquely address this session. Upgrade the remote service before renaming it.");
44 }
45 await ports.renameRemote(target.hostId, target.workspace, source.name, title);
46 }
47 authority.checkpoint();
48 await refreshProjectTopics(input, authority);
49 }
50
50 lines TYPESCRIPT