1import type { EnvironmentId, ThreadId } from "@t3tools/contracts";
2
3interface TerminalLocationLike {
4 readonly cwd: string;
5 readonly worktreePath: string | null;
6}
7
8interface PendingTerminalLaunchTarget {
9 readonly environmentId: EnvironmentId;
10 readonly threadId: ThreadId;
11 readonly terminalId: string;
12}
13
14export interface PendingTerminalLaunch {
15 readonly cwd: string;
16 readonly worktreePath: string | null;
17 readonly env?: Record<string, string>;
18 readonly initialInput?: string;
19}
20
21const pendingTerminalLaunches = new Map<string, PendingTerminalLaunch>();
22
23function pendingTerminalLaunchKey(input: PendingTerminalLaunchTarget): string {
24 return `${input.environmentId}:${input.threadId}:${input.terminalId}`;
25}
26
27export function stagePendingTerminalLaunch(input: {
28 readonly target: PendingTerminalLaunchTarget;
29 readonly launch: PendingTerminalLaunch;
30}) {
31 pendingTerminalLaunches.set(pendingTerminalLaunchKey(input.target), {
32 cwd: input.launch.cwd,
33 worktreePath: input.launch.worktreePath,
34 env: input.launch.env ? { ...input.launch.env } : undefined,
35 initialInput: input.launch.initialInput,
36 });
37}
38
39export function takePendingTerminalLaunch(
40 target: PendingTerminalLaunchTarget,
41): PendingTerminalLaunch | null {
42 const key = pendingTerminalLaunchKey(target);
43 const launch = pendingTerminalLaunches.get(key) ?? null;
44 if (launch) {
45 pendingTerminalLaunches.delete(key);
46 }
47
48 return launch;
49}
50
51export function resolvePreferredThreadWorktreePath(input: {
52 readonly threadShellWorktreePath: string | null;
53 readonly threadDetailWorktreePath: string | null;
54}): string | null {
55 return input.threadDetailWorktreePath ?? input.threadShellWorktreePath ?? null;
56}
57
58export function resolveTerminalOpenLocation(input: {
59 readonly terminalLocation: TerminalLocationLike | null;
60 readonly activeSessionLocation: TerminalLocationLike | null;
61 readonly workspaceRoot: string;
62 readonly threadShellWorktreePath: string | null;
63 readonly threadDetailWorktreePath: string | null;
64}): {
65 readonly cwd: string;
66 readonly worktreePath: string | null;
67} {
68 const preferredThreadWorktreePath = resolvePreferredThreadWorktreePath({
69 threadShellWorktreePath: input.threadShellWorktreePath,
70 threadDetailWorktreePath: input.threadDetailWorktreePath,
71 });
72
73 return {
74 cwd:
75 input.terminalLocation?.cwd ??
76 input.activeSessionLocation?.cwd ??
77 preferredThreadWorktreePath ??
78 input.workspaceRoot,
79 worktreePath:
80 input.terminalLocation?.worktreePath ??
81 input.activeSessionLocation?.worktreePath ??
82 preferredThreadWorktreePath,
83 };
84}