Epicenter
Local-first, open source apps
Terminal is a shell command the agent runs, such as a lint, a test run or a build. The line shows the command, and its output streams in underneath while it runs.
Terminal is built with Trace with a Trace.Collapsible for each command.
Installation
Terminal is built with the Trace and Code Block components.
Usage
<script lang="ts">
import * as CodeBlock from "$lib/components/ui/code-block/index.js";
import * as Trace from "$lib/components/ui/trace/index.js";
</script> <Trace.Root>
<Trace.Collapsible>
<Trace.CollapsibleTrigger>
<Trace.Line>
<Trace.Icon>
<TerminalIcon />
</Trace.Icon>
<Trace.Title>Ran pnpm build</Trace.Title>
</Trace.Line>
</Trace.CollapsibleTrigger>
<Trace.CollapsibleContent>
<CodeBlock.Root size="sm">
<CodeBlock.Content wrap class="max-h-64" code={output} />
</CodeBlock.Root>
</Trace.CollapsibleContent>
</Trace.Collapsible>
</Trace.Root> Exit Code
A command that exits with any code other than 0 failed. Set status="error".
> acme-chat@0.1.0 lint
> eslint .
app/api/chat/route.ts
14:7 error 'model' is assigned a value but never used no-unused-vars
components/chat.tsx
42:5 error React Hook useEffect has a missing dependency: 'messages' react-hooks/exhaustive-deps
✖ 2 problems (2 errors, 0 warnings)
ELIFECYCLE Command failed with exit code 1.The command is a tool call, so its states map the same way as in Tool.
Open While Running
Open the line once the first output arrives, so the output is visible while the command runs. Leave it open or closed after that, as in Trace.
AI SDK
A tool result arrives only once the command exits. To stream the output while it runs, write it from the tool as a data part with the tool call's id. The AI SDK replaces a data part that has the same id, so the output grows in place.
writer.write({
type: "data-terminal",
id: toolCallId,
data: { output },
}); On the client, find the data part that matches each tool part.
function getOutput(message: ChatMessage, toolCallId: string) {
for (const part of message.parts) {
if (part.type === "data-terminal" && part.id === toolCallId) {
return part.data.output;
}
}
return "";
} API Reference
See Trace.