diff --git a/bun.lock b/bun.lock
index 5b3de5f68..d3d8be49b 100644
--- a/bun.lock
+++ b/bun.lock
@@ -67,7 +67,7 @@
"date-fns": "^4.1.0",
"date-fns-tz": "^3.2.0",
"deepmerge": "^4.3.1",
- "e2b": "^2.7.0",
+ "e2b": "^2.10.2",
"echarts": "^6.0.0",
"echarts-for-react": "^3.0.2",
"fast-xml-parser": "^4.5.1",
@@ -157,11 +157,11 @@
},
},
"overrides": {
- "@nodelib/fs.scandir": "2.1.5",
- "@nodelib/fs.stat": "2.0.5",
"@shikijs/core": "2.3.2",
"@shikijs/themes": "2.3.2",
"shiki": "2.3.2",
+ "@nodelib/fs.stat": "2.0.5",
+ "@nodelib/fs.scandir": "2.1.5",
"whatwg-url": "^13",
},
"packages": {
@@ -1617,7 +1617,7 @@
"duplexify": ["duplexify@4.1.3", "", { "dependencies": { "end-of-stream": "^1.4.1", "inherits": "^2.0.3", "readable-stream": "^3.1.1", "stream-shift": "^1.0.2" } }, "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA=="],
- "e2b": ["e2b@2.7.0", "", { "dependencies": { "@bufbuild/protobuf": "^2.6.2", "@connectrpc/connect": "2.0.0-rc.3", "@connectrpc/connect-web": "2.0.0-rc.3", "chalk": "^5.3.0", "compare-versions": "^6.1.0", "dockerfile-ast": "^0.7.1", "glob": "^11.0.3", "openapi-fetch": "^0.14.1", "platform": "^1.3.6", "tar": "^7.4.3" } }, "sha512-pbCbkkdkkY+yIhhtdSE7lM/vhIROtHNI0hNpj8lBphDILNH2qmmjhxU7/wam8/xWRbiWbfuQaOsv100lD32nag=="],
+ "e2b": ["e2b@2.10.2", "", { "dependencies": { "@bufbuild/protobuf": "^2.6.2", "@connectrpc/connect": "2.0.0-rc.3", "@connectrpc/connect-web": "2.0.0-rc.3", "chalk": "^5.3.0", "compare-versions": "^6.1.0", "dockerfile-ast": "^0.7.1", "glob": "^11.1.0", "openapi-fetch": "^0.14.1", "platform": "^1.3.6", "tar": "^7.5.2" } }, "sha512-lKwo5NZ+2v0mAWrqKGAEgSMP/ZPftq7uTtSdYVisVZi4JK9iITtS8SEQLKmRcG+T59/46b//JPvLwluFo1Qi7A=="],
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
diff --git a/package.json b/package.json
index e579091b5..c51290eb2 100644
--- a/package.json
+++ b/package.json
@@ -104,7 +104,7 @@
"date-fns": "^4.1.0",
"date-fns-tz": "^3.2.0",
"deepmerge": "^4.3.1",
- "e2b": "^2.7.0",
+ "e2b": "^2.10.2",
"echarts": "^6.0.0",
"echarts-for-react": "^3.0.2",
"fast-xml-parser": "^4.5.1",
diff --git a/src/features/dashboard/templates/list/table-cells.tsx b/src/features/dashboard/templates/list/table-cells.tsx
index 444922341..7072619b0 100644
--- a/src/features/dashboard/templates/list/table-cells.tsx
+++ b/src/features/dashboard/templates/list/table-cells.tsx
@@ -25,8 +25,9 @@ import {
import { Loader } from '@/ui/primitives/loader_d'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { CellContext } from '@tanstack/react-table'
-import { Lock, LockOpen, MoreVertical } from 'lucide-react'
-import { useParams } from 'next/navigation'
+import { Hammer, Lock, LockOpen, MoreVertical } from 'lucide-react'
+import { useParams, useRouter } from 'next/navigation'
+import { PROTECTED_URLS } from '@/configs/urls'
import { useMemo, useState } from 'react'
import ResourceUsage from '../../common/resource-usage'
import { useDashboard } from '../../context'
@@ -55,7 +56,9 @@ export function ActionsCell({
const { toast } = useToast()
const trpc = useTRPC()
const queryClient = useQueryClient()
+ const router = useRouter()
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false)
+ const [isRebuildDialogOpen, setIsRebuildDialogOpen] = useState(false)
const updateTemplateMutation = useMutation(
trpc.templates.updateTemplate.mutationOptions({
@@ -172,8 +175,40 @@ export function ActionsCell({
})
)
+ const rebuildTemplateMutation = useMutation(
+ trpc.templates.rebuildTemplate.mutationOptions({
+ onSuccess: async (data) => {
+ const templateName = template.aliases[0] || template.templateID
+ toast(
+ defaultSuccessToast(
+ <>
+ Rebuild started for template{' '}
+ {templateName}.
+ >
+ )
+ )
+
+ router.push(
+ PROTECTED_URLS.TEMPLATE_BUILD(teamIdOrSlug, data.templateID, data.buildID)
+ )
+ },
+ onError: (error) => {
+ const templateName = template.aliases[0] || template.templateID
+ toast(
+ defaultErrorToast(
+ error.message || `Failed to rebuild template ${templateName}.`
+ )
+ )
+ },
+ onSettled: () => {
+ setIsRebuildDialogOpen(false)
+ },
+ })
+ )
+
const isUpdating = updateTemplateMutation.isPending
const isDeleting = deleteTemplateMutation.isPending
+ const isRebuilding = rebuildTemplateMutation.isPending
const togglePublish = () => {
updateTemplateMutation.mutate({
@@ -190,6 +225,15 @@ export function ActionsCell({
})
}
+ const rebuildTemplate = () => {
+ rebuildTemplateMutation.mutate({
+ teamIdOrSlug: team.slug ?? team.id,
+ alias: template.aliases[0] || template.templateID,
+ cpuCount: template.cpuCount,
+ memoryMB: template.memoryMB,
+ })
+ }
+
return (
<>
+
+ You are about to rebuild the template{' '}
+ {template.aliases[0] && (
+ <>
+
+ {template.aliases[0]}
+ {' '}
+ (
+ >
+ )}
+
+ {template.templateID}
+
+ {template.aliases[0] && <>)>}. This will create a new build with
+ the same configuration.
+ >
+ }
+ confirm="Rebuild"
+ onConfirm={() => rebuildTemplate()}
+ confirmProps={{
+ variant: 'default',
+ disabled: isRebuilding,
+ loading: isRebuilding,
+ }}
+ />
+