open-swe/ui/src/components/InstructionsEditor.tsx
Johannes du Plessis 3992d3ef5d
feat: repo-scoped dynamic sandbox snapshots (#1595)
* feat: repo-scoped dynamic sandbox snapshots

Let admins build a per-repo sandbox image from a custom Dockerfile so runs
targeting that repo boot from a snapshot with its deps pre-baked. Snapshot
selection is purely additive: repos without a `ready` repo-scoped snapshot
always fall back to the configured DEFAULT_SANDBOX_SNAPSHOT_ID.

Backend adds a repo_snapshots store module (Dockerfile + build status keyed by
owner/name), threads the resolved repo through the LangSmith sandbox creation
path, runs builds via SandboxClient.create_snapshot_from_dockerfile in a
throwaway builder sandbox, and exposes admin-only CRUD + build endpoints. The
UI adds an admin-only Agents-tab page (repo picker + Monaco Dockerfile editor +
build status/logs).

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* fix: harden repo snapshot builds

Require REPO_SNAPSHOT_BASE_IMAGE for generated Dockerfile templates so admins
cannot accidentally build a repo snapshot from a bare Python image that lacks
Open SWE's sandbox tools. Allow stale building records to be retried by tracking
build_started_at and treating old or missing timestamps as stale.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* fix: document repo snapshot base image config

Document REPO_SNAPSHOT_BASE_IMAGE alongside sandbox snapshot setup and convert
missing base-image configuration into a handled dashboard API error so admins see
a clear configuration message instead of an unhandled template-generation error.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-23 12:24:11 -07:00

61 lines
1.4 KiB
TypeScript

import Editor from "@monaco-editor/react"
import { useEffect, useState } from "react"
import { Textarea } from "@/components/ui/textarea"
interface InstructionsEditorProps {
value: string
onChange: (value: string) => void
disabled?: boolean
placeholder?: string
language?: string
}
/** Monaco-backed code editor that falls back to a textarea before mount (SSR-safe). */
export function InstructionsEditor({
value,
onChange,
disabled,
placeholder,
language = "markdown",
}: InstructionsEditorProps) {
const [mounted, setMounted] = useState(false)
useEffect(() => {
setMounted(true)
}, [])
if (!mounted) {
return (
<Textarea
className="min-h-[360px] w-full font-mono text-xs"
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder={placeholder}
disabled={disabled}
/>
)
}
return (
<div className="overflow-hidden rounded-md border border-border">
<Editor
height="360px"
language={language}
value={value}
onChange={(v) => onChange(v ?? "")}
options={{
readOnly: disabled,
minimap: { enabled: false },
wordWrap: "on",
fontSize: 12,
lineNumbers: "on",
scrollBeyondLastLine: false,
padding: { top: 12, bottom: 12 },
renderLineHighlight: "none",
}}
theme="vs-dark"
/>
</div>
)
}