Fix legacy workspace compatibility
This commit is contained in:
@@ -150,12 +150,17 @@ describe("terminal-session-controller restore", () => {
|
||||
});
|
||||
});
|
||||
|
||||
function listSession(input: { id: string; name: string; cwd: string }): TerminalSession {
|
||||
function listSession(input: {
|
||||
id: string;
|
||||
name: string;
|
||||
cwd: string;
|
||||
workspaceId?: string;
|
||||
}): TerminalSession {
|
||||
return {
|
||||
id: input.id,
|
||||
name: input.name,
|
||||
cwd: input.cwd,
|
||||
workspaceId: "ws-test",
|
||||
workspaceId: input.workspaceId ?? "ws-test",
|
||||
send: vi.fn(),
|
||||
subscribe: () => vi.fn(),
|
||||
onExit: () => vi.fn(),
|
||||
@@ -176,6 +181,86 @@ function listSession(input: { id: string; name: string; cwd: string }): Terminal
|
||||
};
|
||||
}
|
||||
|
||||
describe("terminal-session-controller legacy terminal creation", () => {
|
||||
test("resolves a missing workspaceId from the active workspace root", async () => {
|
||||
const rootCwd = "/work/repo";
|
||||
const appCwd = "/work/repo/packages/app";
|
||||
const terminalCwd = "/work/repo/packages/app/src";
|
||||
const outboundMessages: SessionOutboundMessage[] = [];
|
||||
const createTerminal = vi.fn(
|
||||
async (options: Parameters<TerminalManager["createTerminal"]>[0]) =>
|
||||
listSession({
|
||||
id: "term-1",
|
||||
name: options.name ?? "Terminal 1",
|
||||
cwd: options.cwd,
|
||||
workspaceId: options.workspaceId,
|
||||
}),
|
||||
);
|
||||
const terminalManager: TerminalManager = {
|
||||
getTerminals: vi.fn(),
|
||||
createTerminal,
|
||||
registerCwdEnv: vi.fn(),
|
||||
validateTerminalActivityToken: vi.fn(() => "unknown"),
|
||||
getTerminal: vi.fn(),
|
||||
getTerminalState: vi.fn(),
|
||||
setTerminalTitle: vi.fn(),
|
||||
setTerminalActivity: vi.fn(),
|
||||
clearTerminalAttention: vi.fn(),
|
||||
killTerminal: vi.fn(),
|
||||
killTerminalAndWait: vi.fn(),
|
||||
captureTerminal: vi.fn(),
|
||||
listDirectories: vi.fn(() => []),
|
||||
killAll: vi.fn(),
|
||||
subscribeTerminalsChanged: vi.fn(() => vi.fn()),
|
||||
subscribeTerminalActivity: vi.fn(() => vi.fn()),
|
||||
subscribeTerminalWorkspaceContributionChanged: vi.fn(() => vi.fn()),
|
||||
};
|
||||
const controller = new TerminalSessionController({
|
||||
terminalManager,
|
||||
emit: (message) => outboundMessages.push(message),
|
||||
emitBinary: vi.fn(),
|
||||
hasBinaryChannel: () => true,
|
||||
isPathWithinRoot: isSameOrDescendantPath,
|
||||
sessionLogger: createLogger(),
|
||||
listTerminalWorkspaceRefs: async () => [
|
||||
{ workspaceId: "ws-root", cwd: rootCwd },
|
||||
{ workspaceId: "ws-app", cwd: appCwd },
|
||||
],
|
||||
});
|
||||
|
||||
await controller.dispatch({
|
||||
type: "create_terminal_request",
|
||||
cwd: terminalCwd,
|
||||
name: "App Shell",
|
||||
requestId: "req-1",
|
||||
});
|
||||
|
||||
expect(createTerminal).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
cwd: terminalCwd,
|
||||
workspaceId: "ws-app",
|
||||
name: "App Shell",
|
||||
}),
|
||||
);
|
||||
expect(outboundMessages).toEqual([
|
||||
{
|
||||
type: "create_terminal_response",
|
||||
payload: {
|
||||
terminal: {
|
||||
id: "term-1",
|
||||
name: "App Shell",
|
||||
cwd: terminalCwd,
|
||||
workspaceId: "ws-app",
|
||||
activity: null,
|
||||
},
|
||||
error: null,
|
||||
requestId: "req-1",
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
async function flushMicrotasks(): Promise<void> {
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ export interface TerminalSessionControllerOptions {
|
||||
hasBinaryChannel: () => boolean;
|
||||
isPathWithinRoot: (rootPath: string, candidatePath: string) => boolean;
|
||||
sessionLogger: pino.Logger;
|
||||
listTerminalWorkspaceRefs?: () => Promise<readonly TerminalWorkspaceRef[]>;
|
||||
listTerminalWorkspaceRoots?: () => Promise<readonly string[]>;
|
||||
// Whether the connected client can reflow restored snapshots. When true the
|
||||
// daemon attaches per-row soft-wrap flags to snapshots; otherwise it omits them
|
||||
@@ -83,6 +84,11 @@ export interface TerminalSessionControllerOptions {
|
||||
getClientBufferedAmount?: () => number | null;
|
||||
}
|
||||
|
||||
interface TerminalWorkspaceRef {
|
||||
workspaceId: string;
|
||||
cwd: string;
|
||||
}
|
||||
|
||||
export interface TerminalSessionControllerMetrics {
|
||||
directorySubscriptionCount: number;
|
||||
streamSubscriptionCount: number;
|
||||
@@ -120,6 +126,7 @@ export class TerminalSessionController {
|
||||
private readonly hasBinaryChannel: () => boolean;
|
||||
private readonly isPathWithinRoot: (rootPath: string, candidatePath: string) => boolean;
|
||||
private readonly sessionLogger: pino.Logger;
|
||||
private readonly listTerminalWorkspaceRefs: () => Promise<readonly TerminalWorkspaceRef[]>;
|
||||
private readonly listTerminalWorkspaceRoots: () => Promise<readonly string[]>;
|
||||
private readonly clientSupportsWrapReflow: () => boolean;
|
||||
private readonly getClientBufferedAmount: () => number | null;
|
||||
@@ -145,7 +152,10 @@ export class TerminalSessionController {
|
||||
this.hasBinaryChannel = options.hasBinaryChannel;
|
||||
this.isPathWithinRoot = options.isPathWithinRoot;
|
||||
this.sessionLogger = options.sessionLogger;
|
||||
this.listTerminalWorkspaceRoots = options.listTerminalWorkspaceRoots ?? (async () => []);
|
||||
this.listTerminalWorkspaceRefs = options.listTerminalWorkspaceRefs ?? (async () => []);
|
||||
this.listTerminalWorkspaceRoots =
|
||||
options.listTerminalWorkspaceRoots ??
|
||||
(async () => (await this.listTerminalWorkspaceRefs()).map((workspace) => workspace.cwd));
|
||||
this.clientSupportsWrapReflow = options.clientSupportsWrapReflow ?? (() => false);
|
||||
this.getClientBufferedAmount = options.getClientBufferedAmount ?? (() => 0);
|
||||
}
|
||||
@@ -520,7 +530,8 @@ export class TerminalSessionController {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!msg.workspaceId) {
|
||||
const workspaceId = msg.workspaceId ?? (await this.resolveLegacyTerminalWorkspaceId(msg.cwd));
|
||||
if (!workspaceId) {
|
||||
this.emit({
|
||||
type: "create_terminal_response",
|
||||
payload: {
|
||||
@@ -534,7 +545,7 @@ export class TerminalSessionController {
|
||||
|
||||
const session = await this.terminalManager.createTerminal({
|
||||
cwd: msg.cwd,
|
||||
workspaceId: msg.workspaceId,
|
||||
workspaceId,
|
||||
name: msg.name,
|
||||
command: msg.command,
|
||||
args: msg.args,
|
||||
@@ -568,6 +579,31 @@ export class TerminalSessionController {
|
||||
}
|
||||
}
|
||||
|
||||
private async resolveLegacyTerminalWorkspaceId(cwd: string): Promise<string | null> {
|
||||
const workspaceRefs = await this.listTerminalWorkspaceRefs();
|
||||
if (workspaceRefs.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const exactMatch = workspaceRefs.find((workspace) => this.isSamePath(workspace.cwd, cwd));
|
||||
if (exactMatch) {
|
||||
return exactMatch.workspaceId;
|
||||
}
|
||||
|
||||
const ownerRoot = this.resolveTerminalOwnerRoot(
|
||||
cwd,
|
||||
workspaceRefs.map((workspace) => workspace.cwd),
|
||||
);
|
||||
if (!ownerRoot) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
workspaceRefs.find((workspace) => this.isSamePath(workspace.cwd, ownerRoot))?.workspaceId ??
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
private async handleRenameTerminalRequest(msg: RenameTerminalRequest): Promise<void> {
|
||||
const respond = (success: boolean, error: string | null): void => {
|
||||
this.emit({
|
||||
|
||||
Reference in New Issue
Block a user