feat(export-import): add yaml export/import with id upsert for credentials, snippets, and scenarios
- all entities export a kind field (credential/snippet/scenario) for safe type checking on import - import upserts by id: overwrites if id exists, creates with explicit id otherwise - scenario export now includes id and step ids; import deletes old steps before recreating - add GET /:id/export and POST /import endpoints to credential and snippet controllers - add UuidBadge ui component: shortens uuid to first+last 4 hex chars, tooltip, clipboard copy with animated ClipboardCheck icon pop - apply UuidBadge across all entity id display sites (detail pages, card footers, table columns) - add export/import buttons to CredentialsPage, SnippetsPage, CredentialDetailPage, SnippetDetailPage
This commit is contained in:
@@ -135,7 +135,7 @@ export class McpService {
|
||||
{
|
||||
description: "Delete a session by numeric ID (closes it first if open)",
|
||||
inputSchema: {
|
||||
id: z.number().int().describe("Session ID to delete"),
|
||||
id: z.string().uuid().describe("Session ID to delete"),
|
||||
},
|
||||
},
|
||||
async ({ id }) => {
|
||||
@@ -200,7 +200,7 @@ export class McpService {
|
||||
{
|
||||
description: "Get an environment record by ID",
|
||||
inputSchema: {
|
||||
id: z.number().int().describe("Environment ID"),
|
||||
id: z.string().uuid().describe("Environment ID"),
|
||||
},
|
||||
},
|
||||
async ({ id }) => {
|
||||
@@ -258,7 +258,7 @@ export class McpService {
|
||||
{
|
||||
description: "Update an existing environment (name and/or urls)",
|
||||
inputSchema: {
|
||||
id: z.number().int().describe("Environment ID to update"),
|
||||
id: z.string().uuid().describe("Environment ID to update"),
|
||||
name: z.string().optional().describe("New name"),
|
||||
urls: z
|
||||
.record(z.string(), z.string())
|
||||
@@ -289,7 +289,7 @@ export class McpService {
|
||||
{
|
||||
description: "Delete an environment by ID",
|
||||
inputSchema: {
|
||||
id: z.number().int().describe("Environment ID to delete"),
|
||||
id: z.string().uuid().describe("Environment ID to delete"),
|
||||
},
|
||||
},
|
||||
async ({ id }) => {
|
||||
@@ -443,7 +443,7 @@ export class McpService {
|
||||
{
|
||||
description: "Get a scenario with its steps by ID",
|
||||
inputSchema: {
|
||||
id: z.number().int().describe("Scenario ID"),
|
||||
id: z.string().uuid().describe("Scenario ID"),
|
||||
},
|
||||
},
|
||||
async ({ id }) => {
|
||||
@@ -493,7 +493,7 @@ export class McpService {
|
||||
{
|
||||
description: "Update a scenario name",
|
||||
inputSchema: {
|
||||
id: z.number().int().describe("Scenario ID"),
|
||||
id: z.string().uuid().describe("Scenario ID"),
|
||||
name: z.string().optional().describe("New name"),
|
||||
},
|
||||
},
|
||||
@@ -519,7 +519,7 @@ export class McpService {
|
||||
{
|
||||
description: "Delete a scenario by ID",
|
||||
inputSchema: {
|
||||
id: z.number().int().describe("Scenario ID"),
|
||||
id: z.string().uuid().describe("Scenario ID"),
|
||||
},
|
||||
},
|
||||
async ({ id }) => {
|
||||
@@ -544,7 +544,7 @@ export class McpService {
|
||||
{
|
||||
description: "Add a step to a scenario",
|
||||
inputSchema: {
|
||||
scenarioId: z.number().int().describe("Parent scenario ID"),
|
||||
scenarioId: z.string().uuid().describe("Parent scenario ID"),
|
||||
order: z
|
||||
.number()
|
||||
.int()
|
||||
@@ -582,8 +582,8 @@ export class McpService {
|
||||
{
|
||||
description: "Get a single step of a scenario",
|
||||
inputSchema: {
|
||||
scenarioId: z.number().int().describe("Scenario ID"),
|
||||
stepId: z.number().int().describe("Step ID"),
|
||||
scenarioId: z.string().uuid().describe("Scenario ID"),
|
||||
stepId: z.string().uuid().describe("Step ID"),
|
||||
},
|
||||
},
|
||||
async ({ scenarioId, stepId }) => {
|
||||
@@ -606,8 +606,8 @@ export class McpService {
|
||||
{
|
||||
description: "Update a step within a scenario",
|
||||
inputSchema: {
|
||||
scenarioId: z.number().int().describe("Scenario ID"),
|
||||
stepId: z.number().int().describe("Step ID"),
|
||||
scenarioId: z.string().uuid().describe("Scenario ID"),
|
||||
stepId: z.string().uuid().describe("Step ID"),
|
||||
order: z
|
||||
.number()
|
||||
.int()
|
||||
@@ -647,8 +647,8 @@ export class McpService {
|
||||
{
|
||||
description: "Delete a step from a scenario",
|
||||
inputSchema: {
|
||||
scenarioId: z.number().int().describe("Scenario ID"),
|
||||
stepId: z.number().int().describe("Step ID"),
|
||||
scenarioId: z.string().uuid().describe("Scenario ID"),
|
||||
stepId: z.string().uuid().describe("Step ID"),
|
||||
},
|
||||
},
|
||||
async ({ scenarioId, stepId }) => {
|
||||
@@ -677,7 +677,7 @@ export class McpService {
|
||||
description:
|
||||
"List runs for a scenario (paginated, optionally filtered by status)",
|
||||
inputSchema: {
|
||||
scenarioId: z.number().int().describe("Scenario ID"),
|
||||
scenarioId: z.string().uuid().describe("Scenario ID"),
|
||||
status: z
|
||||
.enum(["pending", "in_progress", "pass", "fail"])
|
||||
.optional()
|
||||
@@ -720,7 +720,7 @@ export class McpService {
|
||||
{
|
||||
description: "Trigger an immediate run of a scenario by ID",
|
||||
inputSchema: {
|
||||
id: z.number().int().describe("Scenario ID to run"),
|
||||
id: z.string().uuid().describe("Scenario ID to run"),
|
||||
},
|
||||
},
|
||||
async ({ id }) => {
|
||||
@@ -744,8 +744,8 @@ export class McpService {
|
||||
description:
|
||||
"Get a specific scenario run with all step runs and their outputs",
|
||||
inputSchema: {
|
||||
scenarioId: z.number().int().describe("Scenario ID"),
|
||||
runId: z.number().int().describe("Run ID"),
|
||||
scenarioId: z.string().uuid().describe("Scenario ID"),
|
||||
runId: z.string().uuid().describe("Run ID"),
|
||||
},
|
||||
},
|
||||
async ({ scenarioId, runId }) => {
|
||||
@@ -769,8 +769,8 @@ export class McpService {
|
||||
description:
|
||||
"Block until a scenario run reaches pass or fail (max 5 min), then return the full run with step outputs",
|
||||
inputSchema: {
|
||||
scenarioId: z.number().int().describe("Scenario ID"),
|
||||
runId: z.number().int().describe("Run ID"),
|
||||
scenarioId: z.string().uuid().describe("Scenario ID"),
|
||||
runId: z.string().uuid().describe("Run ID"),
|
||||
},
|
||||
},
|
||||
async ({ scenarioId, runId }) => {
|
||||
@@ -794,7 +794,7 @@ export class McpService {
|
||||
description:
|
||||
"Export a scenario as a portable JSON payload (name + steps)",
|
||||
inputSchema: {
|
||||
id: z.number().int().describe("Scenario ID to export"),
|
||||
id: z.string().uuid().describe("Scenario ID to export"),
|
||||
},
|
||||
},
|
||||
async ({ id }) => {
|
||||
@@ -826,7 +826,7 @@ export class McpService {
|
||||
z.object({
|
||||
order: z.number().int().min(0).describe("Execution order"),
|
||||
type: z.enum(["login", "exec", "sign"]).describe("Step type"),
|
||||
sessionName: z.string().describe("Session name"),
|
||||
title: z.string().nullable().optional().describe("Step title"),
|
||||
execCode: z
|
||||
.string()
|
||||
.nullable()
|
||||
|
||||
Reference in New Issue
Block a user