feat: run workspace operations in terminals

This commit is contained in:
Federico Jaramillo Martinez
2026-05-25 15:01:56 +02:00
parent 57a6a4a69f
commit 711c4f3d98
34 changed files with 1631 additions and 300 deletions
+81
View File
@@ -27,6 +27,16 @@ export function registerTerminalProxyRoutes(app: FastifyInstance, projects: Proj
}
});
app.post<{ Params: { projectId: string; workspaceId: string; terminalId: string } }>("/api/projects/:projectId/workspaces/:workspaceId/terminals/:terminalId/continue", async (request, reply) => {
try {
await resolveWorkspaceContext(projects, workspaces, request.params.projectId, request.params.workspaceId);
return await proxyJson(daemon, "POST", `/terminals/${encodeURIComponent(request.params.terminalId)}/continue`, undefined, reply);
} catch (error) {
requestFailed(reply, error);
return undefined;
}
});
app.delete<{ Params: { projectId: string; workspaceId: string; terminalId: string } }>("/api/projects/:projectId/workspaces/:workspaceId/terminals/:terminalId", async (request, reply) => {
try {
await resolveWorkspaceContext(projects, workspaces, request.params.projectId, request.params.workspaceId);
@@ -37,6 +47,51 @@ export function registerTerminalProxyRoutes(app: FastifyInstance, projects: Proj
}
});
app.post<{ Params: { projectId: string; workspaceId: string }; Body: TerminalCommandRunRequest }>("/api/projects/:projectId/workspaces/:workspaceId/terminal-command-runs", async (request, reply) => {
try {
const context = await resolveWorkspaceContext(projects, workspaces, request.params.projectId, request.params.workspaceId);
return await proxyJson(daemon, "POST", "/terminal-command-runs", {
origin: request.body.origin,
projectId: request.params.projectId,
workspaceId: request.params.workspaceId,
cwd: context.root,
title: request.body.title,
command: request.body.command,
metadata: request.body.metadata ?? {},
}, reply);
} catch (error) {
requestFailed(reply, error);
return undefined;
}
});
app.get<{ Querystring: TerminalCommandRunQuery }>("/api/terminal-command-runs", async (request, reply) => {
try {
return await proxyJson(daemon, "GET", `/terminal-command-runs${terminalCommandRunQuery(request.query)}`, undefined, reply);
} catch (error) {
requestFailed(reply, error);
return undefined;
}
});
app.post<{ Params: { runId: string } }>("/api/terminal-command-runs/:runId/cancel", async (request, reply) => {
try {
return await proxyJson(daemon, "POST", `/terminal-command-runs/${encodeURIComponent(request.params.runId)}/cancel`, undefined, reply);
} catch (error) {
requestFailed(reply, error);
return undefined;
}
});
app.get<{ Params: { runId: string } }>("/api/terminal-command-runs/:runId", async (request, reply) => {
try {
return await proxyJson(daemon, "GET", `/terminal-command-runs/${encodeURIComponent(request.params.runId)}`, undefined, reply);
} catch (error) {
requestFailed(reply, error);
return undefined;
}
});
app.get<{ Params: { projectId: string; workspaceId: string; terminalId: string }; Querystring: { cols?: string; rows?: string } }>("/api/projects/:projectId/workspaces/:workspaceId/terminals/:terminalId/socket", { websocket: true }, async (socket, request) => {
try {
await resolveWorkspaceContext(projects, workspaces, request.params.projectId, request.params.workspaceId);
@@ -49,6 +104,32 @@ export function registerTerminalProxyRoutes(app: FastifyInstance, projects: Proj
});
}
interface TerminalCommandRunRequest {
origin: string;
title: string;
command: string;
metadata?: Record<string, string>;
}
interface TerminalCommandRunQuery {
projectId?: string;
workspaceId?: string;
terminalId?: string;
statuses?: string;
metadata?: string;
}
function terminalCommandRunQuery(filter: TerminalCommandRunQuery): string {
const params = new URLSearchParams();
if (filter.projectId !== undefined) params.set("projectId", filter.projectId);
if (filter.workspaceId !== undefined) params.set("workspaceId", filter.workspaceId);
if (filter.terminalId !== undefined) params.set("terminalId", filter.terminalId);
if (filter.statuses !== undefined) params.set("statuses", filter.statuses);
if (filter.metadata !== undefined) params.set("metadata", filter.metadata);
const query = params.toString();
return query === "" ? "" : `?${query}`;
}
async function proxyJson(daemon: SessionDaemonClient, method: string, path: string, body: unknown, reply: FastifyReply): Promise<unknown> {
const upstream = await daemon.request(method, path, body);
reply.code(upstream.statusCode);