diff --git a/.env.example b/.env.example index d65d729..5e6a842 100644 --- a/.env.example +++ b/.env.example @@ -17,7 +17,9 @@ DAEMON_NAME=Local MacBook DAEMON_VERSION=0.0.0 DAEMON_HEARTBEAT_MS=15000 DAEMON_COMMAND_LEASE_MS=60000 -# RIVET_ENDPOINT=http://localhost:6420 +RIVET_ENDPOINT=http://localhost:6420 +RIVET_PUBLIC_ENDPOINT=http://localhost:6420 +RIVET_WORKSPACE_TOKEN=replace-with-a-long-random-workspace-token # Flue persistence adapter FLUE_DB_TOKEN=replace-with-a-long-random-token diff --git a/apps/web/src/components/slice-one/slice-one-page.tsx b/apps/web/src/components/slice-one/slice-one-page.tsx index 9729f88..019ce87 100644 --- a/apps/web/src/components/slice-one/slice-one-page.tsx +++ b/apps/web/src/components/slice-one/slice-one-page.tsx @@ -5,8 +5,10 @@ import { } from "@code/ui/components/ai-elements/conversation"; import { Button } from "@code/ui/components/button"; import { + AlertTriangle, ChevronRight, Check, + FileCode2, FolderGit2, Hammer, LoaderCircle, @@ -15,6 +17,7 @@ import { ImagePlus, Play, RotateCcw, + ScrollText, Send, Sparkles, Settings, @@ -36,6 +39,28 @@ import { type SliceWork = NonNullable["works"]>[number]; const EMPTY_WORKS: readonly SliceWork[] = []; +type SliceArtifact = NonNullable< + NonNullable[number] +>; + +interface ArtifactMetadata { + readonly changedFiles?: readonly string[]; +} + +const parseArtifactMetadata = (artifact: SliceArtifact): ArtifactMetadata => { + if (!artifact.metadataJson) { + return {}; + } + try { + return JSON.parse(artifact.metadataJson) as ArtifactMetadata; + } catch { + return {}; + } +}; + +const changedFilesFor = (artifact: SliceArtifact): readonly string[] => + parseArtifactMetadata(artifact).changedFiles ?? []; + interface WorkCardProps { readonly onSourceSelect: (rawText: string) => void; readonly work: SliceWork; @@ -96,6 +121,7 @@ const starterDesign = (work: SliceWork) => ({ const WorkCard = ({ onSourceSelect, work, slice }: WorkCardProps) => { const [sourcesOpen, setSourcesOpen] = useState(false); const [expanded, setExpanded] = useState(false); + const [logsOpen, setLogsOpen] = useState(false); const sources = work.signals.flatMap((signal) => signal.sources); const { definition } = work; const { design } = work; @@ -258,6 +284,22 @@ const WorkCard = ({ onSourceSelect, work, slice }: WorkCardProps) => {

Build

+ {slice.operationError ? ( +

+ + + {slice.operationError.message} + + +

+ ) : null} {latestRun ? (

@@ -269,31 +311,75 @@ const WorkCard = ({ onSourceSelect, work, slice }: WorkCardProps) => { latestRun.terminalClassification ?? "activity is still arriving"}

- {latestRun.attemptEvents?.slice(-5).map((item) => ( -

- {item.message} -

- ))} {latestRun.baseRevision ? (

{latestRun.baseRevision.slice(0, 8)} โ†’{" "} {latestRun.candidateRevision?.slice(0, 8) ?? "working"}

) : null} - {latestRun.artifacts?.map((artifact) => ( - - {artifact.title} - - ))} + {latestRun.artifacts?.map((artifact) => { + const changedFiles = changedFilesFor(artifact); + return ( +
+ {artifact.uri ? ( + + {artifact.title} + + ) : ( +

{artifact.title}

+ )} + {changedFiles.length > 0 ? ( +
    + {changedFiles.map((file) => ( +
  • + + {file} +
  • + ))} +
+ ) : null} +
+ ); + })} + {latestRun.attemptEvents && + latestRun.attemptEvents.length > 0 ? ( +
+ {(logsOpen + ? latestRun.attemptEvents + : latestRun.attemptEvents.slice(-3) + ).map((item) => ( +

+ {item.message} +

+ ))} + {latestRun.attemptEvents.length > 3 ? ( + + ) : null} +
+ ) : null}
) : (

No implementation Run yet.

@@ -303,9 +389,7 @@ const WorkCard = ({ onSourceSelect, work, slice }: WorkCardProps) => { @@ -315,11 +399,7 @@ const WorkCard = ({ onSourceSelect, work, slice }: WorkCardProps) => { size="sm" variant="outline" onClick={() => - void slice.startSimulation( - work._id, - "success", - design?.slices?.[0]?.id - ) + void slice.startSimulation(work._id, "success") } > Simulate @@ -338,7 +418,8 @@ const WorkCard = ({ onSourceSelect, work, slice }: WorkCardProps) => { Cancel ) : null} - {latestRun?.status === "terminal" && + {latestRun?.executionKind !== "real" && + latestRun?.status === "terminal" && latestRun.terminalClassification === "RetryableFailure" ? ( +

+ ) : null}