Fix MCP terminal capture scrollback
This commit is contained in:
@@ -22,6 +22,7 @@ import {
|
||||
import type { CreatePaseoWorktreeWorkflowFn } from "../worktree-session.js";
|
||||
import { WorkspaceGitServiceImpl } from "../workspace-git-service.js";
|
||||
import type { GitHubService } from "../../services/github-service.js";
|
||||
import type { TerminalManager } from "../../terminal/terminal-manager.js";
|
||||
|
||||
interface LooseSafeParseResult {
|
||||
success: boolean;
|
||||
@@ -136,6 +137,23 @@ function createTestDeps(): TestDeps {
|
||||
};
|
||||
}
|
||||
|
||||
function createTerminalManagerStub(overrides: Partial<TerminalManager> = {}): TerminalManager {
|
||||
return {
|
||||
getTerminals: vi.fn().mockResolvedValue([]),
|
||||
createTerminal: vi.fn(),
|
||||
registerCwdEnv: vi.fn(),
|
||||
getTerminal: vi.fn(),
|
||||
getTerminalState: vi.fn().mockResolvedValue(null),
|
||||
killTerminal: vi.fn(),
|
||||
killTerminalAndWait: vi.fn().mockResolvedValue(undefined),
|
||||
captureTerminal: vi.fn().mockResolvedValue({ lines: [], totalLines: 0 }),
|
||||
listDirectories: vi.fn().mockReturnValue([]),
|
||||
killAll: vi.fn(),
|
||||
subscribeTerminalsChanged: vi.fn().mockReturnValue(() => {}),
|
||||
...overrides,
|
||||
} as unknown as TerminalManager;
|
||||
}
|
||||
|
||||
function createProviderDefinition(overrides: Partial<ProviderDefinition>): ProviderDefinition {
|
||||
const provider = overrides.id ?? "claude";
|
||||
return {
|
||||
@@ -339,6 +357,53 @@ function createPaseoWorktreeForMcpTest(options: {
|
||||
};
|
||||
}
|
||||
|
||||
describe("terminal MCP tools", () => {
|
||||
const logger = createTestLogger();
|
||||
|
||||
it("captures terminal output through the terminal manager authority", async () => {
|
||||
const { agentManager, agentStorage } = createTestDeps();
|
||||
const captureTerminal = vi.fn().mockResolvedValue({
|
||||
lines: ["from worker scrollback"],
|
||||
totalLines: 42,
|
||||
});
|
||||
const terminalManager = createTerminalManagerStub({
|
||||
getTerminal: vi.fn().mockReturnValue({
|
||||
id: "term-1",
|
||||
name: "daemon",
|
||||
cwd: process.cwd(),
|
||||
getState: vi.fn().mockReturnValue({ scrollback: [], grid: [[]] }),
|
||||
}),
|
||||
captureTerminal,
|
||||
});
|
||||
const server = await createAgentMcpServer({
|
||||
agentManager,
|
||||
agentStorage,
|
||||
terminalManager,
|
||||
logger,
|
||||
});
|
||||
const tool = registeredTool(server, "capture_terminal");
|
||||
|
||||
const response = await tool.callback({
|
||||
terminalId: "term-1",
|
||||
scrollback: true,
|
||||
stripAnsi: false,
|
||||
start: -10,
|
||||
end: -1,
|
||||
});
|
||||
|
||||
expect(captureTerminal).toHaveBeenCalledWith("term-1", {
|
||||
start: 0,
|
||||
end: -1,
|
||||
stripAnsi: false,
|
||||
});
|
||||
expect(response.structuredContent).toEqual({
|
||||
terminalId: "term-1",
|
||||
lines: ["from worker scrollback"],
|
||||
totalLines: 42,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("create_agent MCP tool", () => {
|
||||
const logger = createTestLogger();
|
||||
const existingCwd = process.cwd();
|
||||
|
||||
@@ -39,7 +39,6 @@ import { scheduleAgentMetadataGeneration } from "./agent-metadata-generator.js";
|
||||
import type { VoiceCallerContext, VoiceSpeakHandler } from "../voice-types.js";
|
||||
import { expandUserPath, isSameOrDescendantPath, resolvePathFromBase } from "../path-utils.js";
|
||||
import type { TerminalManager } from "../../terminal/terminal-manager.js";
|
||||
import { captureTerminalLines } from "../../terminal/terminal-capture.js";
|
||||
import type {
|
||||
AgentWorktreeSetupContinuation,
|
||||
CreatePaseoWorktreeSetupContinuationInput,
|
||||
@@ -1433,12 +1432,11 @@ export async function createAgentMcpServer(options: AgentMcpServerOptions): Prom
|
||||
throw new Error("Terminal manager is not configured");
|
||||
}
|
||||
|
||||
const terminal = terminalManager.getTerminal(terminalId);
|
||||
if (!terminal) {
|
||||
if (!terminalManager.getTerminal(terminalId)) {
|
||||
throw new Error(`Terminal ${terminalId} not found`);
|
||||
}
|
||||
|
||||
const capture = captureTerminalLines(terminal, {
|
||||
const capture = await terminalManager.captureTerminal(terminalId, {
|
||||
start: scrollback ? 0 : start,
|
||||
end,
|
||||
stripAnsi,
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import {
|
||||
captureTerminalLines,
|
||||
createTerminal,
|
||||
type CaptureTerminalLinesResult,
|
||||
type TerminalSession,
|
||||
type TerminalStateSnapshot,
|
||||
} from "./terminal.js";
|
||||
import { createTerminal, type TerminalSession, type TerminalStateSnapshot } from "./terminal.js";
|
||||
import { captureTerminalLines, type CaptureTerminalLinesResult } from "./terminal-capture.js";
|
||||
import { resolve, sep, win32, posix } from "node:path";
|
||||
|
||||
export interface TerminalListItem {
|
||||
|
||||
@@ -8,11 +8,6 @@ import { createRequire } from "node:module";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { createExternalProcessEnv } from "../server/paseo-env.js";
|
||||
import type { TerminalCell, TerminalState } from "../shared/messages.js";
|
||||
export {
|
||||
captureTerminalLines,
|
||||
type CaptureTerminalLinesOptions,
|
||||
type CaptureTerminalLinesResult,
|
||||
} from "./terminal-capture.js";
|
||||
|
||||
const { Terminal } = xterm;
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
@@ -4,13 +4,13 @@ import { randomUUID } from "node:crypto";
|
||||
import type { TerminalState } from "../shared/messages.js";
|
||||
import type {
|
||||
ClientMessage,
|
||||
CaptureTerminalLinesResult,
|
||||
ServerMessage,
|
||||
TerminalCommandFinishedInfo,
|
||||
TerminalExitInfo,
|
||||
TerminalSession,
|
||||
TerminalStateSnapshot,
|
||||
} from "./terminal.js";
|
||||
import type { CaptureTerminalLinesResult } from "./terminal-capture.js";
|
||||
import type {
|
||||
TerminalListItem,
|
||||
TerminalManager,
|
||||
|
||||
Reference in New Issue
Block a user