// frontend/components/AdminTabs/SandboxTab.jsx import React, { useCallback, useEffect, useState } from "react"; import { apiUrl } from "../../utils/api.js"; import MatrixLabInstallModal from "./MatrixLabInstallModal.jsx"; /** * Sandbox tab — enterprise default view. * * Surfaces a single MatrixLab addon card plus a clean Local-sandbox * card. Implementation knobs (Runner URL, token, image, network, * timeout, pass-through, lifecycle env flag) live behind the install * modal's Advanced disclosure — the primary view never shows them. * * Status pill is driven off /api/matrixlab/status so we never end up * with the "Unreachable + Running" contradiction the legacy panel * produced. */ function StatusPill({ status }) { const map = { not_installed: { label: "Not installed", bg: "#374151", fg: "#d1d5db" }, installing: { label: "Installing", bg: "#0d3320", fg: "#86efac" }, starting: { label: "Starting", bg: "#0d3320", fg: "#86efac" }, stopping: { label: "Stopping", bg: "#3d2d11", fg: "#fde68a" }, checking: { label: "Checking", bg: "#0d3320", fg: "#86efac" }, ready: { label: "Ready", bg: "#0d3320", fg: "#86efac" }, needs_attention: { label: "Needs attention", bg: "#3d2d11", fg: "#fde68a" }, failed: { label: "Failed", bg: "#3d1111", fg: "#fca5a5" }, }; const pill = map[status] || map.not_installed; return ( {pill.label} ); } export default function SandboxTab({ showToast }) { const [matrixlab, setMatrixlab] = useState(null); // /api/matrixlab/status payload const [sandbox, setSandbox] = useState(null); // /api/sandbox/status payload const [loading, setLoading] = useState(true); const [modalOpen, setModalOpen] = useState(false); const load = useCallback(async () => { try { const [ml, sb] = await Promise.all([ fetch(apiUrl("/api/matrixlab/status")).then((r) => r.json()).catch(() => null), fetch(apiUrl("/api/sandbox/status")).then((r) => r.json()).catch(() => null), ]); setMatrixlab(ml); setSandbox(sb); } finally { setLoading(false); } }, []); useEffect(() => { load(); }, [load]); const useLocal = useCallback(async () => { try { const r = await fetch(apiUrl("/api/sandbox/config"), { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ backend: "subprocess" }), }); const data = await r.json(); if (r.ok) { setSandbox(data); showToast?.("Switched to Local sandbox", "Code runs in a host subprocess with a workspace jail."); } } catch (err) { // surfaced through the next load() } }, [showToast]); if (loading) { return (
Pick where GitPilot executes code: the local subprocess sandbox or the MatrixLab Runner. The choice applies to the Run button on chat code blocks and the agent's EXECUTE action.
{/* MatrixLab addon card */}Run code safely in isolated, temporary containers. Recommended for enterprise deployments and untrusted code.
{mlStatus !== "not_installed" && matrixlab?.message && ({matrixlab.message}
)}Host subprocess with a workspace jail. No Docker required — best for trying simple snippets quickly.