Add interactive PTY Chat tab with xterm.js terminal emulator
Browser-based interactive terminal sessions with agent CLIs via WebSocket + node-pty. Supports full TUI rendering (colors, cursor, ctrl-c) through xterm.js in the browser. Architecture: xterm.js ←WebSocket→ pty-server.js ←PTY→ agent CLI - Extract shared buildAgentEnv() from executor into agent-env.ts - Add internal /api/agents/[id]/env endpoint for PTY server - Add pty-server.js (WebSocket + node-pty, max 3 sessions, 2hr cleanup) - Add custom server.js wrapping Next.js with WebSocket upgrade - Add ChatTab component with agent selector and terminal - Wire CHAT tab into dashboard nav and render - Configure serverExternalPackages for node-pty - Update Dockerfile with build tools and custom server - Bump k8s memory limit 1Gi → 2Gi for PTY sessions
This commit is contained in:
40
apps/harness/src/app/api/agents/[id]/env/route.ts
vendored
Normal file
40
apps/harness/src/app/api/agents/[id]/env/route.ts
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { getAgentConfig } from "@/lib/agents";
|
||||
import { buildAgentEnv, buildInteractiveCommand } from "@/lib/agent-env";
|
||||
|
||||
export async function GET(
|
||||
_request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
// Only allow localhost access
|
||||
const forwarded = _request.headers.get("x-forwarded-for");
|
||||
const host = _request.headers.get("host") ?? "";
|
||||
const isLocal =
|
||||
!forwarded &&
|
||||
(host.startsWith("localhost") || host.startsWith("127.0.0.1"));
|
||||
if (!isLocal) {
|
||||
return NextResponse.json({ error: "forbidden" }, { status: 403 });
|
||||
}
|
||||
|
||||
const { id } = await params;
|
||||
const config = await getAgentConfig(id);
|
||||
if (!config) {
|
||||
return NextResponse.json({ error: "agent not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
const env = await buildAgentEnv(config);
|
||||
const { command, args } = buildInteractiveCommand(config);
|
||||
|
||||
// Strip process.env inherited values — only send what we explicitly set
|
||||
const cleanEnv: Record<string, string> = {};
|
||||
for (const [k, v] of Object.entries(env)) {
|
||||
if (v !== undefined) cleanEnv[k] = v;
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
command,
|
||||
args,
|
||||
env: cleanEnv,
|
||||
workDir: process.env.HARNESS_WORK_DIR || "/data/harness",
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user