diff --git a/apps/factory/agent/lib/sandbox-ssh.ts b/apps/factory/agent/lib/sandbox-ssh.ts index 4856cfd483c5c..830e0341d26f2 100644 --- a/apps/factory/agent/lib/sandbox-ssh.ts +++ b/apps/factory/agent/lib/sandbox-ssh.ts @@ -1,3 +1,9 @@ export function sandboxSshCommand(name: string): string { return `sandbox ssh ${name}`; } + +const SSHABLE_STATUSES = new Set(["running", "stopped"]); + +export function isSandboxSSHable(status: string): boolean { + return SSHABLE_STATUSES.has(status); +} diff --git a/apps/factory/app/api/sandbox/terminal/route.ts b/apps/factory/app/api/sandbox/terminal/route.ts index d7fbfa54cb01a..6c7fd3f72c81c 100644 --- a/apps/factory/app/api/sandbox/terminal/route.ts +++ b/apps/factory/app/api/sandbox/terminal/route.ts @@ -3,5 +3,14 @@ import { handleTerminalRequest } from "@/agent/lib/sandbox-terminal"; export const dynamic = "force-dynamic"; export async function POST(request: Request): Promise { + if ( + request.headers.get("origin") !== new URL(request.url).origin || + request.headers.get("content-type")?.split(";", 1)[0] !== "application/json" + ) + return Response.json( + { error: "Invalid terminal request." }, + { status: 403 } + ); + return handleTerminalRequest(request); } diff --git a/apps/factory/app/run-observatory.tsx b/apps/factory/app/run-observatory.tsx index 9c239f852f58e..7983674155e15 100644 --- a/apps/factory/app/run-observatory.tsx +++ b/apps/factory/app/run-observatory.tsx @@ -9,7 +9,7 @@ import { useState } from "react"; -import { sandboxSshCommand } from "../agent/lib/sandbox-ssh"; +import { isSandboxSSHable, sandboxSshCommand } from "../agent/lib/sandbox-ssh"; import type { AgentRunRecord, ControlPlaneSnapshot, @@ -68,8 +68,16 @@ function duration(run: AgentRunRecord): string { return `${Math.round(milliseconds / 60_000)}m`; } -function RunTicket({ run }: { readonly run: AgentRunRecord }) { +function RunTicket({ + run, + onTerminal +}: { + readonly run: AgentRunRecord; + readonly onTerminal: (name: string) => void; +}) { const detailsUrl = run.source === "eve" ? AGENT_RUNS_URL : WORKFLOW_RUNS_URL; + const sandbox = run.sandbox; + const sshable = sandbox ? isSandboxSSHable(sandbox.status) : false; return (
  • @@ -112,11 +120,28 @@ function RunTicket({ run }: { readonly run: AgentRunRecord }) { {run.sandbox?.status ?? run.status} - {run.sandbox && run.sandbox.provider !== "eve" ? ( - + {sandbox && sandbox.provider !== "eve" ? ( + <> + + + ) : null}