diff --git a/backend/version.py b/backend/version.py index 432dcb9..48f9192 100644 --- a/backend/version.py +++ b/backend/version.py @@ -1,3 +1,3 @@ """Single source of truth for the StackPilot release version.""" -APP_VERSION = "0.33.0" +APP_VERSION = "0.35.0" diff --git a/frontend/package.json b/frontend/package.json index b03c0dc..51c5a79 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "stackpilot-frontend", "private": true, - "version": "0.34.1", + "version": "0.35.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/components/stacks/AgentStacksSection.tsx b/frontend/src/components/stacks/AgentStacksSection.tsx index 14d655e..88d2e5e 100644 --- a/frontend/src/components/stacks/AgentStacksSection.tsx +++ b/frontend/src/components/stacks/AgentStacksSection.tsx @@ -81,6 +81,7 @@ export function AgentStacksSection({ agent, isAdmin }: { agent: Agent; isAdmin: onStart={(id) => run("start", "Starting", id)} onStop={(id) => run("stop", "Stopping", id)} onRestart={(id) => run("restart", "Restarting", id)} + onUpdate={isAdmin ? (id) => run("update", "Updating", id) : undefined} emptyText="No stacks on this host." /> )} diff --git a/frontend/src/components/stacks/StacksTable.tsx b/frontend/src/components/stacks/StacksTable.tsx index 2ab1134..34eab49 100644 --- a/frontend/src/components/stacks/StacksTable.tsx +++ b/frontend/src/components/stacks/StacksTable.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import { Link } from "react-router-dom"; import { useQueryClient } from "@tanstack/react-query"; -import { Play, Square, RotateCw, Pencil, Trash2 } from "lucide-react"; +import { Play, Square, RotateCw, ArrowUpCircle, Pencil, Trash2 } from "lucide-react"; import { toast } from "sonner"; import { Card, Spinner, StatusDot, Badge } from "@/components/ui"; import { ConfirmDialog } from "@/components/ui/ConfirmDialog"; @@ -26,6 +26,7 @@ export function StacksTable({ onStart, onStop, onRestart, + onUpdate, emptyText, }: { stacks: StackSummary[] | undefined; @@ -41,6 +42,7 @@ export function StacksTable({ onStart: (id: string) => void; onStop: (id: string) => void; onRestart: (id: string) => void; + onUpdate?: (id: string) => void; emptyText: string; }) { if (loading) return ; @@ -78,6 +80,7 @@ export function StacksTable({ onStart={onStart} onStop={onStop} onRestart={onRestart} + onUpdate={onUpdate} /> ))} @@ -99,6 +102,7 @@ function StackRow({ onStart, onStop, onRestart, + onUpdate, }: { stack: StackSummary; stats?: StackStats; @@ -112,6 +116,7 @@ function StackRow({ onStart: (id: string) => void; onStop: (id: string) => void; onRestart: (id: string) => void; + onUpdate?: (id: string) => void; }) { const qc = useQueryClient(); const running = stack.running_count > 0; @@ -195,6 +200,15 @@ function StackRow({ )} + {onUpdate && ( + onUpdate(stack.id)} + disabled={busy} + > + + + )} {showEdit && ( = { export function Stacks() { const isAdmin = useAuthStore((s) => s.user?.role === "admin"); - const { busyId, start, stop, restart } = useStackActions(); + const { busyId, start, stop, restart, updateImages } = useStackActions(); // The dashboard explore bar deep-links here with ?q= / ?filter=. const [params] = useSearchParams(); const [q, setQ] = useState(params.get("q") ?? ""); @@ -137,6 +137,7 @@ export function Stacks() { onStart={start} onStop={stop} onRestart={restart} + onUpdate={isAdmin ? updateImages : undefined} emptyText={q ? "No stacks match your search." : "No stacks yet. Create one with “New Stack”."} />