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”."}
/>