diff --git a/README.md b/README.md index 14f7f5c..c502b8b 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Docs: https://rivet.dev/docs/ - **Universal session schema**: Universal schema to store agent transcripts - **Supports your sandbox provider**: Daytona, E2B, Vercel Sandboxes, and more - **Lightweight, portable Rust binary**: Install anywhere with 1 curl command +- **Automatic agent installation**: Agents are installed on-demand when first used - **OpenAPI spec**: https://rivet.dev/docs/api Roadmap: @@ -16,8 +17,6 @@ Roadmap: - [ ] Python SDK - [ ] Automatic MCP & skill & hook configuration - [ ] Todo lists -- [ ] Session diff -- [ ] Subagents ## Agent Compatibility diff --git a/examples/daytona/daytona.ts b/examples/daytona/daytona.ts index 4c23149..1b23ffc 100644 --- a/examples/daytona/daytona.ts +++ b/examples/daytona/daytona.ts @@ -1,5 +1,6 @@ import { Daytona } from "@daytonaio/sdk"; -import { pathToFileURL } from "node:url"; +import { pathToFileURL, fileURLToPath } from "node:url"; +import { dirname, resolve } from "node:path"; import { ensureUrl, logInspectorUrl, @@ -7,8 +8,8 @@ import { waitForHealth, } from "../shared/sandbox-agent-client.ts"; -const INSTALL_SCRIPT = "curl -fsSL https://releases.rivet.dev/sandbox-agent/latest/install.sh | sh"; const DEFAULT_PORT = 3000; +const BINARY_PATH = resolve(dirname(fileURLToPath(import.meta.url)), "../../target/release/sandbox-agent"); export async function setupDaytonaSandboxAgent(): Promise<{ baseUrl: string; @@ -21,57 +22,47 @@ export async function setupDaytonaSandboxAgent(): Promise<{ const language = process.env.DAYTONA_LANGUAGE || "typescript"; const daytona = new Daytona(); - const sandbox = await daytona.create({ - language, - }); + console.log("Creating sandbox..."); + const sandbox = await daytona.create({ language }); - await sandbox.process.executeCommand(`bash -lc "${INSTALL_SCRIPT}"`); + console.log("Uploading sandbox-agent..."); + await sandbox.fs.uploadFile(BINARY_PATH, "/home/daytona/sandbox-agent"); + await sandbox.fs.setFilePermissions("/home/daytona/sandbox-agent", { mode: "755" }); - const tokenFlag = token ? "--token $SANDBOX_TOKEN" : "--no-token"; - const serverCommand = `nohup sandbox-agent server ${tokenFlag} --host 0.0.0.0 --port ${port} >/tmp/sandbox-agent.log 2>&1 &`; - await sandbox.process.executeCommand(`bash -lc "${serverCommand}"`); + console.log("Starting server..."); + const tokenFlag = token ? `--token ${token}` : "--no-token"; + await sandbox.process.executeCommand( + `nohup /home/daytona/sandbox-agent server ${tokenFlag} --host 0.0.0.0 --port ${port} >/tmp/sandbox-agent.log 2>&1 &` + ); const preview = await sandbox.getPreviewLink(port); - const extraHeaders: Record = {}; + const extraHeaders: Record = { + "x-daytona-skip-preview-warning": "true", + }; if (preview.token) { extraHeaders["x-daytona-preview-token"] = preview.token; } - extraHeaders["x-daytona-skip-preview-warning"] = "true"; const baseUrl = ensureUrl(preview.url); + console.log("Waiting for health..."); await waitForHealth({ baseUrl, token, extraHeaders }); logInspectorUrl({ baseUrl, token }); - const cleanup = async () => { - try { - await sandbox.delete(60); - } catch { - // ignore cleanup errors - } - }; - return { baseUrl, token, extraHeaders, - cleanup, + cleanup: async () => { + try { await sandbox.delete(60); } catch {} + }, }; } async function main(): Promise { const { baseUrl, token, extraHeaders, cleanup } = await setupDaytonaSandboxAgent(); - const exitHandler = async () => { - await cleanup(); - process.exit(0); - }; - - process.on("SIGINT", () => { - void exitHandler(); - }); - process.on("SIGTERM", () => { - void exitHandler(); - }); + process.on("SIGINT", () => void cleanup().then(() => process.exit(0))); + process.on("SIGTERM", () => void cleanup().then(() => process.exit(0))); await runPrompt({ baseUrl, token, extraHeaders }); } diff --git a/frontend/packages/inspector/index.html b/frontend/packages/inspector/index.html index 07e48b2..e8a8313 100644 --- a/frontend/packages/inspector/index.html +++ b/frontend/packages/inspector/index.html @@ -719,7 +719,6 @@ .empty-state .button { margin-top: 16px; - min-width: 180px; width: auto; } @@ -1032,8 +1031,8 @@ /* Setup Row */ .setup-row { display: flex; - align-items: center; - gap: 8px; + align-items: flex-end; + gap: 12px; padding: 8px 12px; background: var(--surface-2); border-top: 1px solid var(--border); @@ -1041,6 +1040,19 @@ flex-wrap: wrap; } + .setup-field { + display: flex; + flex-direction: column; + gap: 4px; + } + + .setup-label { + font-size: 10px; + color: var(--muted); + text-transform: uppercase; + letter-spacing: 0.5px; + } + .setup-select { background: var(--surface); border: 1px solid var(--border-2); diff --git a/frontend/packages/inspector/src/App.tsx b/frontend/packages/inspector/src/App.tsx index 215af70..f7f7c30 100644 --- a/frontend/packages/inspector/src/App.tsx +++ b/frontend/packages/inspector/src/App.tsx @@ -503,16 +503,38 @@ export default function App() { offsetRef.current = 0; }; - const handleCopy = async (entry: RequestLog) => { - try { - await navigator.clipboard.writeText(entry.curl); + const handleCopy = (entry: RequestLog) => { + const text = entry.curl; + const onSuccess = () => { setCopiedLogId(entry.id); window.setTimeout(() => setCopiedLogId(null), 1500); - } catch { - setCopiedLogId(null); + }; + + if (navigator.clipboard && window.isSecureContext) { + navigator.clipboard.writeText(text).then(onSuccess).catch(() => { + fallbackCopy(text, onSuccess); + }); + } else { + fallbackCopy(text, onSuccess); } }; + const fallbackCopy = (text: string, onSuccess?: () => void) => { + const textarea = document.createElement("textarea"); + textarea.value = text; + textarea.style.position = "fixed"; + textarea.style.opacity = "0"; + document.body.appendChild(textarea); + textarea.select(); + try { + document.execCommand("copy"); + onSuccess?.(); + } catch (err) { + console.error("Failed to copy:", err); + } + document.body.removeChild(textarea); + }; + const selectQuestionOption = (requestId: string, optionLabel: string) => { setQuestionSelections((prev) => ({ ...prev, @@ -887,9 +909,7 @@ export default function App() { onDebugTabChange={setDebugTab} events={events} offset={offset} - onFetchEvents={fetchEvents} onResetEvents={resetEvents} - eventsLoading={eventsLoading} eventsError={eventError} requestLog={requestLog} copiedLogId={copiedLogId} diff --git a/frontend/packages/inspector/src/components/chat/ChatSetup.tsx b/frontend/packages/inspector/src/components/chat/ChatSetup.tsx index fe50ba4..ded3825 100644 --- a/frontend/packages/inspector/src/components/chat/ChatSetup.tsx +++ b/frontend/packages/inspector/src/components/chat/ChatSetup.tsx @@ -29,57 +29,69 @@ const ChatSetup = ({ }) => { return (
- +
+ Mode + +
- +
+ Permission + +
- onModelChange(e.target.value)} - placeholder="Model" - title="Model" - disabled={!hasSession} - /> +
+ Model + onModelChange(e.target.value)} + placeholder="Model" + title="Model" + disabled={!hasSession} + /> +
- onVariantChange(e.target.value)} - placeholder="Variant" - title="Variant" - disabled={!hasSession} - /> +
+ Variant + onVariantChange(e.target.value)} + placeholder="Variant" + title="Variant" + disabled={!hasSession} + /> +
); }; diff --git a/frontend/packages/inspector/src/components/debug/DebugPanel.tsx b/frontend/packages/inspector/src/components/debug/DebugPanel.tsx index 7a1beba..9c5de66 100644 --- a/frontend/packages/inspector/src/components/debug/DebugPanel.tsx +++ b/frontend/packages/inspector/src/components/debug/DebugPanel.tsx @@ -12,9 +12,7 @@ const DebugPanel = ({ onDebugTabChange, events, offset, - onFetchEvents, onResetEvents, - eventsLoading, eventsError, requestLog, copiedLogId, @@ -32,9 +30,7 @@ const DebugPanel = ({ onDebugTabChange: (tab: DebugTab) => void; events: UniversalEvent[]; offset: number; - onFetchEvents: () => void; onResetEvents: () => void; - eventsLoading: boolean; eventsError: string | null; requestLog: RequestLog[]; copiedLogId: number | null; @@ -80,9 +76,7 @@ const DebugPanel = ({ )} diff --git a/frontend/packages/inspector/src/components/debug/EventsTab.tsx b/frontend/packages/inspector/src/components/debug/EventsTab.tsx index 8ff2d5e..ba2fc62 100644 --- a/frontend/packages/inspector/src/components/debug/EventsTab.tsx +++ b/frontend/packages/inspector/src/components/debug/EventsTab.tsx @@ -7,16 +7,12 @@ import { getEventCategory, getEventClass, getEventIcon, getEventKey, getEventTyp const EventsTab = ({ events, offset, - onFetch, onClear, - loading, error }: { events: UniversalEvent[]; offset: number; - onFetch: () => void; onClear: () => void; - loading: boolean; error: string | null; }) => { const [collapsedEvents, setCollapsedEvents] = useState>({}); @@ -64,9 +60,6 @@ const EventsTab = ({
Offset: {offset}
-