diff --git a/bin/deploy-runner.js b/bin/deploy-runner.js index 653c970..f9fa907 100644 --- a/bin/deploy-runner.js +++ b/bin/deploy-runner.js @@ -117137,7 +117137,22 @@ async function main() { const flowResult = await mod.buildFlow(scripting); const flowId = typeof flowResult?.id === "string" ? flowResult.id : void 0; const flowName = typeof flowResult?.name === "string" ? flowResult.name : void 0; - emit("result", { success: true, flowId, flowName }); + let warnings; + if (typeof flowResult?.validateAsync === "function") { + const validation = await flowResult.validateAsync(); + if (validation.hasErrorsOrWarnings) { + warnings = []; + for (const issue of validation.issues) { + const label = issue.archObject?.logStr ?? "Unknown"; + for (const err of issue.errors ?? []) + warnings.push(`ERROR [${label}]: ${err}`); + for (const warn of issue.warnings ?? []) + warnings.push(`WARN [${label}]: ${warn}`); + } + for (const w of warnings) emit("log", "warn", w); + } + } + emit("result", { success: true, flowId, flowName, warnings }); } catch (err) { const message = err instanceof Error ? err.message : String(err); emit("result", { success: false, error: message }); diff --git a/package.json b/package.json index 6262b27..6bc266c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "genesys-cloud-architect", - "version": "1.0.2", + "version": "1.0.3", "private": true, "packageManager": "pnpm@11.1.3+sha512.c85357fe17ca12dd23dd7071822666dfd7e3cb76fe214e3370b5ea2fb34f2a231185509b63e717f3cd0acb38dd3f8d82bcd5e8172400ae678b70ea4fbed0896d", "scripts": { diff --git a/servers/genesys-cloud-architect-mcp.js b/servers/genesys-cloud-architect-mcp.js index 0d17c88..dfed348 100644 --- a/servers/genesys-cloud-architect-mcp.js +++ b/servers/genesys-cloud-architect-mcp.js @@ -153,10 +153,13 @@ ${r.join(` Logs: ${r.join(` -`)}`:"";if(s?.success){let f=["Flow deployed successfully."];s.flowId&&f.push(`Flow ID: ${s.flowId}`),s.flowName&&f.push(`Flow Name: ${s.flowName}`),l({content:[{type:"text",text:f.join(` +`)}`:"";if(s?.success){let f=["Flow deployed successfully."];s.flowId&&f.push(`Flow ID: ${s.flowId}`),s.flowName&&f.push(`Flow Name: ${s.flowName}`),s.warnings?.length&&f.push(` +Validation warnings: +${s.warnings.join(` +`)}`),l({content:[{type:"text",text:f.join(` `)+m}]})}else{let f=s?.error??`Deploy runner exited with code ${h}`;l({isError:!0,content:[{type:"text",text:`Deploy failed: ${f}${m}`}]})}})})}});function pI(t){let e=t.toUpperCase();return e.endsWith("FLOW")?e:`${e}FLOW`}function iX(t,e){let i={};for(let n of e){let a=n.type??"UNKNOWN";i[a]||(i[a]=[]),i[a].push({id:n.id??"",name:n.name??"",...n.version?{version:n.version}:{},deleted:n.deleted??!1,updated:n.updated??!1})}return{flow:{id:t.id??"",name:t.name,type:pI(t.type??""),version:t.publishedVersion?.commitVersion??"1"},dependencies:i}}var dI=({architectApi:t})=>({config:{description:"Retrieves all dependencies consumed by a Genesys Cloud Architect flow. Returns the flow metadata and its dependencies grouped by type.",annotations:{title:"Flow Dependencies",readOnlyHint:!0,destructiveHint:!1},inputSchema:{flowId:Qi.string().min(1).describe("The Genesys Cloud Architect flow ID")}},handler:async({flowId:e})=>{try{let i;try{i=await t.getFlow(e)}catch{return{isError:!0,content:[{type:"text",text:`Flow "${e}" not found.`}]}}let n=pI(i.type??""),a=i.publishedVersion?.commitVersion??"1",r=[],s=1;for(;;){let l=await t.getArchitectDependencytrackingConsumedresources(i.id,a,n,{pageSize:100,pageNumber:s});if(l.entities&&r.push(...l.entities),!l.nextUri)break;s++}let o=iX(i,r);return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}catch(i){return{isError:!0,content:[{type:"text",text:`Failed to retrieve flow dependencies: ${i instanceof Error?i.message:String(i)}`}]}}}});var ry=Qi.object({GENESYS_REGION:Qi.string().min(1),GENESYS_CLIENT_ID:Qi.string().min(1),GENESYS_CLIENT_SECRET:Qi.string().min(1),DEPLOY_SCRIPT_PATH:Qi.string().min(1),PREVENT_LOGIN:Qi.enum(["TRUE","FALSE"]).default("FALSE").transform(t=>t==="TRUE")}).safeParse(process.env);if(!ry.success){let t=ry.error.issues.map(e=>e.path[0]).join(` `);console.error(`Missing required environment variables: - ${t}`),process.exit(1)}var Kt=ry.data,oy=new Cc({name:"genesys-cloud-architect",version:"1.0.2"}),hI=dI({architectApi:new sy.default.ArchitectApi});oy.registerTool("flow_dependencies",hI.config,hI.handler);var gI=cI({region:Kt.GENESYS_REGION,clientId:Kt.GENESYS_CLIENT_ID,clientSecret:Kt.GENESYS_CLIENT_SECRET,deployScriptPath:Kt.DEPLOY_SCRIPT_PATH});oy.registerTool("deploy_flow",gI.config,gI.handler);(async()=>{if(Kt.PREVENT_LOGIN)console.warn("Login for Platform API skipped. Calling tools will result in an auth failure.");else{let e=sy.default.ApiClient.instance;e.setEnvironment(Kt.GENESYS_REGION),await e.loginClientCredentialsGrant(Kt.GENESYS_CLIENT_ID,Kt.GENESYS_CLIENT_SECRET)}let t=new bc;await oy.connect(t)})().catch(t=>{console.error("Failed to start server:",t),process.exit(1)}); + ${t}`),process.exit(1)}var Kt=ry.data,oy=new Cc({name:"genesys-cloud-architect",version:"1.0.3"}),hI=dI({architectApi:new sy.default.ArchitectApi});oy.registerTool("flow_dependencies",hI.config,hI.handler);var gI=cI({region:Kt.GENESYS_REGION,clientId:Kt.GENESYS_CLIENT_ID,clientSecret:Kt.GENESYS_CLIENT_SECRET,deployScriptPath:Kt.DEPLOY_SCRIPT_PATH});oy.registerTool("deploy_flow",gI.config,gI.handler);(async()=>{if(Kt.PREVENT_LOGIN)console.warn("Login for Platform API skipped. Calling tools will result in an auth failure.");else{let e=sy.default.ApiClient.instance;e.setEnvironment(Kt.GENESYS_REGION),await e.loginClientCredentialsGrant(Kt.GENESYS_CLIENT_ID,Kt.GENESYS_CLIENT_SECRET)}let t=new bc;await oy.connect(t)})().catch(t=>{console.error("Failed to start server:",t),process.exit(1)}); /*! Bundled license information: mime-db/index.js: diff --git a/skills/write-flow/references/examples/inbound-call.md b/skills/write-flow/references/examples/inbound-call.md index c18872c..c1fcb21 100644 --- a/skills/write-flow/references/examples/inbound-call.md +++ b/skills/write-flow/references/examples/inbound-call.md @@ -5,7 +5,7 @@ Simple IVR with a welcome message, main menu with 3 choices, and queue transfers ```typescript import type { ArchitectScripting } from "purecloud-flow-scripting-api-sdk-javascript"; -export async function buildFlow(scripting: ArchitectScripting): Promise { +export async function buildFlow(scripting: ArchitectScripting) { const { archFactoryFlows, archFactoryActions, archFactoryMenus } = scripting.factories; const flow = await archFactoryFlows.createFlowInboundCallAsync( @@ -40,6 +40,6 @@ export async function buildFlow(scripting: ArchitectScripting): Promise { const jumpToMenu = archFactoryActions.addActionJumpToMenu(initialState.outputSequence); jumpToMenu.targetMenu = mainMenu; - await flow.checkInAsync(); + return await flow.checkInAsync(); } ``` diff --git a/skills/write-flow/references/examples/inbound-chat.md b/skills/write-flow/references/examples/inbound-chat.md index 320f2f4..f4f2c03 100644 --- a/skills/write-flow/references/examples/inbound-chat.md +++ b/skills/write-flow/references/examples/inbound-chat.md @@ -5,7 +5,7 @@ Chat flow with a greeting message and queue transfer. ```typescript import type { ArchitectScripting } from "purecloud-flow-scripting-api-sdk-javascript"; -export async function buildFlow(scripting: ArchitectScripting): Promise { +export async function buildFlow(scripting: ArchitectScripting) { const { archFactoryFlows, archFactoryActions } = scripting.factories; const flow = await archFactoryFlows.createFlowInboundChatAsync( @@ -25,6 +25,6 @@ export async function buildFlow(scripting: ArchitectScripting): Promise { const transfer = archFactoryActions.addActionTransferToAcd(initialState); await transfer.setQueueByName("Chat Support Queue"); - await flow.checkInAsync(); + return await flow.checkInAsync(); } ``` diff --git a/skills/write-flow/references/examples/inbound-email.md b/skills/write-flow/references/examples/inbound-email.md index 3313692..3798a7d 100644 --- a/skills/write-flow/references/examples/inbound-email.md +++ b/skills/write-flow/references/examples/inbound-email.md @@ -5,7 +5,7 @@ Email flow with auto-reply and queue transfer. ```typescript import type { ArchitectScripting } from "purecloud-flow-scripting-api-sdk-javascript"; -export async function buildFlow(scripting: ArchitectScripting): Promise { +export async function buildFlow(scripting: ArchitectScripting) { const { archFactoryFlows, archFactoryActions } = scripting.factories; const flow = await archFactoryFlows.createFlowInboundEmailAsync( @@ -28,6 +28,6 @@ export async function buildFlow(scripting: ArchitectScripting): Promise { const transfer = archFactoryActions.addActionTransferToAcd(sequence); await transfer.setQueueByName("Email Support Queue"); - await flow.checkInAsync(); + return await flow.checkInAsync(); } ``` diff --git a/skills/write-flow/references/examples/inbound-message.md b/skills/write-flow/references/examples/inbound-message.md index b93a170..f5a4067 100644 --- a/skills/write-flow/references/examples/inbound-message.md +++ b/skills/write-flow/references/examples/inbound-message.md @@ -5,7 +5,7 @@ SMS/messaging flow with auto-reply and queue transfer. ```typescript import type { ArchitectScripting } from "purecloud-flow-scripting-api-sdk-javascript"; -export async function buildFlow(scripting: ArchitectScripting): Promise { +export async function buildFlow(scripting: ArchitectScripting) { const { archFactoryFlows, archFactoryActions } = scripting.factories; const flow = await archFactoryFlows.createFlowInboundShortMessageAsync( @@ -25,6 +25,6 @@ export async function buildFlow(scripting: ArchitectScripting): Promise { const transfer = archFactoryActions.addActionTransferToAcd(initialState); await transfer.setQueueByName("SMS Support Queue"); - await flow.checkInAsync(); + return await flow.checkInAsync(); } ``` diff --git a/skills/write-flow/references/examples/workflow.md b/skills/write-flow/references/examples/workflow.md index 425e3a4..bb71eb6 100644 --- a/skills/write-flow/references/examples/workflow.md +++ b/skills/write-flow/references/examples/workflow.md @@ -5,7 +5,7 @@ Automated workflow that creates a callback request. ```typescript import type { ArchitectScripting } from "purecloud-flow-scripting-api-sdk-javascript"; -export async function buildFlow(scripting: ArchitectScripting): Promise { +export async function buildFlow(scripting: ArchitectScripting) { const { archFactoryFlows, archFactoryActions } = scripting.factories; const flow = await archFactoryFlows.createFlowWorkflowAsync( @@ -33,6 +33,6 @@ export async function buildFlow(scripting: ArchitectScripting): Promise { // End workflow archFactoryActions.addActionEndWorkflow(sequence); - await flow.checkInAsync(); + return await flow.checkInAsync(); } ``` diff --git a/skills/write-flow/references/sdk-patterns.md b/skills/write-flow/references/sdk-patterns.md index a14d84a..49118b5 100644 --- a/skills/write-flow/references/sdk-patterns.md +++ b/skills/write-flow/references/sdk-patterns.md @@ -20,7 +20,7 @@ Every flow file must export this function: ```typescript import type { ArchitectScripting } from "purecloud-flow-scripting-api-sdk-javascript"; -export async function buildFlow(scripting: ArchitectScripting): Promise { +export async function buildFlow(scripting: ArchitectScripting) { const { archFactoryFlows, archFactoryActions } = scripting.factories; const flow = await archFactoryFlows.createFlowInboundCallAsync( @@ -30,14 +30,14 @@ export async function buildFlow(scripting: ArchitectScripting): Promise { // Build the flow logic... - await flow.checkInAsync(); + return await flow.checkInAsync(); } ``` - The SDK instance is passed as a parameter — never `require()` it - Only use `import type` from the SDK package - The deploy runner handles authentication and session management -- Call `flow.checkInAsync()` to save the flow to Genesys Cloud +- Return `await flow.checkInAsync()` — this saves the flow and returns the flow object, which the deploy runner uses to run `validateAsync()` and report validation warnings/errors ## Flow Creation Methods diff --git a/src/deploy-runner/index.ts b/src/deploy-runner/index.ts index 44ed4f0..7784034 100644 --- a/src/deploy-runner/index.ts +++ b/src/deploy-runner/index.ts @@ -18,6 +18,7 @@ function emit( success: boolean; flowId?: string; flowName?: string; + warnings?: string[]; error?: string; }, ): void; @@ -337,7 +338,23 @@ async function main(): Promise { const flowName = typeof flowResult?.name === "string" ? flowResult.name : undefined; - emit("result", { success: true, flowId, flowName }); + let warnings: string[] | undefined; + if (typeof flowResult?.validateAsync === "function") { + const validation = await flowResult.validateAsync(); + if (validation.hasErrorsOrWarnings) { + warnings = []; + for (const issue of validation.issues) { + const label = issue.archObject?.logStr ?? "Unknown"; + for (const err of issue.errors ?? []) + warnings.push(`ERROR [${label}]: ${err}`); + for (const warn of issue.warnings ?? []) + warnings.push(`WARN [${label}]: ${warn}`); + } + for (const w of warnings) emit("log", "warn", w); + } + } + + emit("result", { success: true, flowId, flowName, warnings }); } catch (err) { const message = err instanceof Error ? err.message : String(err); emit("result", { success: false, error: message }); diff --git a/src/mcp-server/tools/deploy-flow.ts b/src/mcp-server/tools/deploy-flow.ts index 806dff8..424a338 100644 --- a/src/mcp-server/tools/deploy-flow.ts +++ b/src/mcp-server/tools/deploy-flow.ts @@ -11,6 +11,7 @@ interface DeployRunnerLine { success?: boolean; flowId?: string; flowName?: string; + warnings?: string[]; error?: string; } @@ -164,6 +165,10 @@ export const deployFlow: ToolFactory = (toolConfig) => ({ parts.push(`Flow ID: ${resultLine.flowId}`); if (resultLine.flowName) parts.push(`Flow Name: ${resultLine.flowName}`); + if (resultLine.warnings?.length) + parts.push( + `\nValidation warnings:\n${resultLine.warnings.join("\n")}`, + ); settle({ content: [