diff --git a/apps/cli/bin/app.js b/apps/cli/bin/app.js index 5fdf3ffc..8be66d23 100755 --- a/apps/cli/bin/app.js +++ b/apps/cli/bin/app.js @@ -37,15 +37,33 @@ yargs(hideBin(process.argv)) } ) .command( - "import-example ", - "Import an example workflow by name", - (y) => y.positional("example", { - type: "string", - description: "The example to import", - }), + "import", + "Import an example workflow (--example) or custom workflow from file (--file)", + (y) => y + .option("example", { + type: "string", + description: "Name of built-in example to import", + }) + .option("file", { + type: "string", + description: "Path to custom workflow JSON file", + }) + .check((argv) => { + if (!argv.example && !argv.file) { + throw new Error("Either --example or --file must be provided"); + } + if (argv.example && argv.file) { + throw new Error("Cannot use both --example and --file at the same time"); + } + return true; + }), async (argv) => { try { - await importExample(String(argv.example).trim()); + if (argv.example) { + await importExample(String(argv.example).trim()); + } else if (argv.file) { + await importExample(undefined, String(argv.file).trim()); + } } catch (error) { console.error("Error:", error?.message ?? error); process.exit(1); diff --git a/apps/cli/src/app.ts b/apps/cli/src/app.ts index 577f8ca9..e4d734ea 100644 --- a/apps/cli/src/app.ts +++ b/apps/cli/src/app.ts @@ -10,6 +10,7 @@ import { createInterface, Interface } from "node:readline/promises"; import { ToolCallPart } from "./application/entities/message.js"; import { Agent } from "./application/entities/agent.js"; import { McpServerConfig, McpServerDefinition } from "./application/entities/mcp.js"; +import { Example } from "./application/entities/example.js"; import { z } from "zod"; import { Flavor } from "./application/entities/models.js"; import { examples } from "./examples/index.js"; @@ -466,15 +467,37 @@ async function mergeMcpServers(servers: Record; + let sourceName: string; + + if (exampleName) { + // Load from built-in examples + example = examples[exampleName]; + if (!example) { + const availableExamples = Object.keys(examples); + const listMessage = availableExamples.length + ? `Available examples: ${availableExamples.join(", ")}` + : "No packaged examples are available."; + throw new Error(`Unknown example '${exampleName}'. ${listMessage}`); + } + sourceName = exampleName; + } else if (filePath) { + // Load from file path + try { + const fileContent = await fsp.readFile(filePath, "utf8"); + example = Example.parse(JSON.parse(fileContent)); + sourceName = path.basename(filePath, ".json"); + } catch (error: any) { + if (error?.code === "ENOENT") { + throw new Error(`File not found: ${filePath}`); + } else if (error?.name === "ZodError") { + throw new Error(`Invalid workflow file format: ${error.message}`); + } + throw new Error(`Failed to read workflow file: ${error.message ?? error}`); + } + } else { + throw new Error("Either exampleName or filePath must be provided"); } // Import agents and MCP servers @@ -489,7 +512,7 @@ export async function importExample(exampleName: string) { const entryAgent = example.entryAgent ?? importedAgents[0] ?? ""; const output = [ - `✓ Imported example '${exampleName}'`, + `✓ Imported workflow '${sourceName}'`, ` Agents: ${importedAgents.join(", ")}`, ` Primary: ${entryAgent}`, ];