fix: support manifest-type tool definitions in Tool Workshop

Backend:
- Allow 'manifest' in tool_types definition_type validators
- Add manifest_id to ToolTypeCreate, ToolTypeUpdate, ToolTypeResponse
- Skip compose/dockerfile template validation when definition_type is manifest
- Require manifest_id when definition_type is manifest
- Clear legacy templates when switching to manifest type

Frontend:
- Load manifest data via getToolDefinition when selecting a manifest-type tool
- Create/update manifest definition via tool-definitions API when saving
- Pass manifest_id to tool-types create/update API
- Fix unused EmptyState import after configs/folders cleanup
This commit is contained in:
Alex Blank
2026-05-28 21:34:25 +02:00
parent 6e4275a510
commit 7e3c701ea6
2 changed files with 100 additions and 13 deletions
+68 -4
View File
@@ -1,6 +1,5 @@
import { useCallback, useEffect, useState } from "react";
import {
EmptyState,
ErrorState,
LoadingState,
} from "../components/data-states";
@@ -22,7 +21,10 @@ import {
type UpdateToolTypeRequest,
} from "../api/tool_types";
import {
createToolDefinition,
getToolDefinition,
listToolDefinitions,
updateToolDefinition,
type ToolDefinitionManifest,
} from "../api/tool_definitions";
import { ManifestEditor } from "../components/manifest-editor";
@@ -120,7 +122,7 @@ export const ToolWorkshopPage = () => {
setManifestDefinitionId(null);
};
const populateToolTypeForm = (toolType: ToolType) => {
const populateToolTypeForm = async (toolType: ToolType) => {
setToolTypeForm({
name: toolType.name,
display_name: toolType.display_name,
@@ -142,9 +144,18 @@ export const ToolWorkshopPage = () => {
});
setToolTypeError(null);
setToolTypeDirty(false);
// For manifest types, we'd need to load the manifest data separately
setManifestData(null);
setManifestDefinitionId(toolType.manifest_id || null);
if (toolType.definition_type === "manifest" && toolType.manifest_id) {
try {
const defn = await getToolDefinition(toolType.manifest_id);
setManifestData(defn.manifest);
} catch {
setManifestData(null);
}
} else {
setManifestData(null);
}
};
const handleSelectToolType = (toolType: ToolType | null) => {
@@ -203,6 +214,9 @@ export const ToolWorkshopPage = () => {
);
return;
}
} else if (!manifestData) {
setToolTypeError("Manifest data is required for manifest definition type");
return;
}
const variables = toolTypeForm.required_variables
@@ -226,6 +240,23 @@ export const ToolWorkshopPage = () => {
try {
if (isCreating) {
let manifestId: string | undefined;
if (toolTypeForm.definition_type === "manifest" && manifestData) {
const manifestPayload = {
name: toolTypeForm.name.trim(),
display_name: toolTypeForm.display_name.trim(),
description: toolTypeForm.description.trim() || undefined,
category: toolTypeForm.category.trim() || undefined,
interface_type: toolTypeForm.interface_type,
base_image: (manifestData.base_image as string) || undefined,
base_definition_id:
(manifestData.base_definition_id as string) || undefined,
manifest: manifestData,
};
const newManifest = await createToolDefinition(manifestPayload);
manifestId = newManifest.id;
}
const input: CreateToolTypeRequest = {
name: toolTypeForm.name.trim(),
display_name: toolTypeForm.display_name.trim(),
@@ -237,6 +268,7 @@ export const ToolWorkshopPage = () => {
? Number(toolTypeForm.default_port)
: 0,
definition_type: toolTypeForm.definition_type,
manifest_id: manifestId,
compose_template:
toolTypeForm.definition_type === "compose" ? template : undefined,
dockerfile_template:
@@ -252,6 +284,34 @@ export const ToolWorkshopPage = () => {
setSelectedToolTypeId(newTool.id);
setToolTypeDirty(false);
} else if (selectedToolType) {
let manifestId = selectedToolType.manifest_id || undefined;
if (toolTypeForm.definition_type === "manifest" && manifestData) {
if (manifestId) {
await updateToolDefinition(manifestId, {
display_name: toolTypeForm.display_name.trim(),
description: toolTypeForm.description.trim() || undefined,
category: toolTypeForm.category.trim() || undefined,
manifest: manifestData,
});
} else {
const manifestPayload = {
name: toolTypeForm.name.trim(),
display_name: toolTypeForm.display_name.trim(),
description: toolTypeForm.description.trim() || undefined,
category: toolTypeForm.category.trim() || undefined,
interface_type: toolTypeForm.interface_type,
base_image:
(manifestData.base_image as string) || undefined,
base_definition_id:
(manifestData.base_definition_id as string) || undefined,
manifest: manifestData,
};
const newManifest =
await createToolDefinition(manifestPayload);
manifestId = newManifest.id;
}
}
const input: UpdateToolTypeRequest = {
display_name: toolTypeForm.display_name.trim(),
description: toolTypeForm.description.trim() || undefined,
@@ -262,6 +322,10 @@ export const ToolWorkshopPage = () => {
? Number(toolTypeForm.default_port)
: 0,
definition_type: toolTypeForm.definition_type,
manifest_id:
toolTypeForm.definition_type === "manifest"
? manifestId
: undefined,
compose_template:
toolTypeForm.definition_type === "compose" ? template : undefined,
dockerfile_template: