chore: update landing and inspector content

This commit is contained in:
Nathan Flurry 2026-01-27 22:29:35 -08:00
parent c7fbb33fed
commit 30c4ad6b39
8 changed files with 128 additions and 129 deletions

View file

@ -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

View file

@ -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<string, string> = {};
const extraHeaders: Record<string, string> = {
"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<void> {
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 });
}

View file

@ -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);

View file

@ -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}

View file

@ -29,57 +29,69 @@ const ChatSetup = ({
}) => {
return (
<div className="setup-row">
<select
className="setup-select"
value={agentMode}
onChange={(e) => onAgentModeChange(e.target.value)}
title="Mode"
disabled={!hasSession || modesLoading || Boolean(modesError)}
>
{modesLoading ? (
<option value="">Loading modes...</option>
) : modesError ? (
<option value="">{modesError}</option>
) : activeModes.length > 0 ? (
activeModes.map((mode) => (
<option key={mode.id} value={mode.id}>
{mode.name || mode.id}
</option>
))
) : (
<option value="">Mode</option>
)}
</select>
<div className="setup-field">
<span className="setup-label">Mode</span>
<select
className="setup-select"
value={agentMode}
onChange={(e) => onAgentModeChange(e.target.value)}
title="Mode"
disabled={!hasSession || modesLoading || Boolean(modesError)}
>
{modesLoading ? (
<option value="">Loading modes...</option>
) : modesError ? (
<option value="">{modesError}</option>
) : activeModes.length > 0 ? (
activeModes.map((mode) => (
<option key={mode.id} value={mode.id}>
{mode.name || mode.id}
</option>
))
) : (
<option value="">Mode</option>
)}
</select>
</div>
<select
className="setup-select"
value={permissionMode}
onChange={(e) => onPermissionModeChange(e.target.value)}
title="Permission Mode"
disabled={!hasSession}
>
<option value="default">Default</option>
<option value="plan">Plan</option>
<option value="bypass">Bypass</option>
</select>
<div className="setup-field">
<span className="setup-label">Permission</span>
<select
className="setup-select"
value={permissionMode}
onChange={(e) => onPermissionModeChange(e.target.value)}
title="Permission Mode"
disabled={!hasSession}
>
<option value="default">Default</option>
<option value="plan">Plan</option>
<option value="bypass">Bypass</option>
</select>
</div>
<input
className="setup-input"
value={model}
onChange={(e) => onModelChange(e.target.value)}
placeholder="Model"
title="Model"
disabled={!hasSession}
/>
<div className="setup-field">
<span className="setup-label">Model</span>
<input
className="setup-input"
value={model}
onChange={(e) => onModelChange(e.target.value)}
placeholder="Model"
title="Model"
disabled={!hasSession}
/>
</div>
<input
className="setup-input"
value={variant}
onChange={(e) => onVariantChange(e.target.value)}
placeholder="Variant"
title="Variant"
disabled={!hasSession}
/>
<div className="setup-field">
<span className="setup-label">Variant</span>
<input
className="setup-input"
value={variant}
onChange={(e) => onVariantChange(e.target.value)}
placeholder="Variant"
title="Variant"
disabled={!hasSession}
/>
</div>
</div>
);
};

View file

@ -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 = ({
<EventsTab
events={events}
offset={offset}
onFetch={onFetchEvents}
onClear={onResetEvents}
loading={eventsLoading}
error={eventsError}
/>
)}

View file

@ -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<Record<string, boolean>>({});
@ -64,9 +60,6 @@ const EventsTab = ({
<div className="inline-row" style={{ marginBottom: 12, justifyContent: "space-between" }}>
<span className="card-meta">Offset: {offset}</span>
<div className="inline-row">
<button className="button ghost small" onClick={onFetch} disabled={loading}>
{loading ? "Loading..." : "Fetch"}
</button>
<button
type="button"
className="button ghost small"
@ -74,7 +67,7 @@ const EventsTab = ({
disabled={events.length === 0}
title="Copy all events as JSON"
>
{copied ? "Copied" : "Copy"}
{copied ? "Copied" : "Copy JSON"}
</button>
<button className="button ghost small" onClick={onClear}>
Clear
@ -86,7 +79,7 @@ const EventsTab = ({
{events.length === 0 ? (
<div className="card-meta">
{loading ? "Loading events..." : "No events yet. Start streaming to receive events."}
No events yet. Start streaming to receive events.
</div>
) : (
<div className="event-list">

View file

@ -1,23 +1 @@
# Vercel AI SDK Schemas
This package extracts JSON Schema for `UIMessage` from the Vercel AI SDK v6 TypeScript types.
## Usage
- Install dependencies in this folder.
- Run the extractor:
```
pnpm install
pnpm extract
```
Optional flags:
- `--version=6.x.y` to pin an exact version
- `--major=6` to select the latest version for a major (default: 6)
Output:
- `artifacts/json-schema/ui-message.json`
The registry response is cached under `.cache/` for 24 hours. The extractor downloads the AI SDK package
and the minimal dependency tree needed for TypeScript type resolution into a temporary folder.
hello