newo
Version:
NEWO CLI: Professional command-line tool with modular architecture for NEWO AI Agent development. Features IDN-based file management, real-time progress tracking, intelligent sync operations, and comprehensive multi-customer support.
153 lines (133 loc) • 5.17 kB
text/typescript
/**
* Metadata and flows.yaml generation operations
*/
import {
writeFileSafe,
flowMetadataPath,
agentMetadataPath,
flowsYamlPath
} from '../fsutil.js';
import fs from 'fs-extra';
import yaml from 'js-yaml';
import type {
ProjectData,
ProjectMap,
FlowsYamlData,
FlowsYamlFlow,
FlowsYamlSkill,
FlowMetadata,
AgentMetadata,
SkillMetadata
} from '../types.js';
/**
* Generate flows.yaml file from project data
*/
export async function generateFlowsYaml(
projectMap: ProjectMap | { [key: string]: ProjectData },
customerIdn: string,
verbose: boolean = false
): Promise<string> {
if (verbose) console.log(`📊 Generating flows.yaml for customer ${customerIdn}...`);
const flowsData: FlowsYamlData = {
flows: []
};
// Handle both formats
const projects = 'projects' in projectMap ? projectMap.projects : projectMap;
for (const [projectIdn, projectData] of Object.entries(projects)) {
if (verbose && projectIdn) console.log(` 📁 Processing project: ${projectIdn}`);
for (const [agentIdn, agentData] of Object.entries(projectData.agents as Record<string, any>)) {
if (verbose) console.log(` 📁 Processing agent: ${agentIdn}`);
const agentFlows: FlowsYamlFlow[] = [];
for (const [flowIdn, flowData] of Object.entries(agentData.flows as Record<string, any>)) {
if (verbose) console.log(` 📁 Processing flow: ${flowIdn}`);
// Load flow metadata to get comprehensive flow information
const flowMetaPath = flowMetadataPath(customerIdn, projectIdn, agentIdn, flowIdn);
let flowMeta: FlowMetadata | null = null;
try {
if (await fs.pathExists(flowMetaPath)) {
const flowMetaContent = await fs.readFile(flowMetaPath, 'utf8');
flowMeta = yaml.load(flowMetaContent) as FlowMetadata;
}
} catch (e) {
if (verbose) console.log(` ⚠️ Could not load flow metadata: ${flowMetaPath}`);
}
const skills: FlowsYamlSkill[] = [];
for (const [, skillMeta] of Object.entries(flowData.skills as Record<string, SkillMetadata>)) {
// Note: We don't need to load script content since prompt_script is excluded from flows.yaml
skills.push({
idn: skillMeta.idn,
title: skillMeta.title,
runner_type: skillMeta.runner_type,
model: skillMeta.model,
parameters: skillMeta.parameters.map((p: any) => ({
name: p.name,
default_value: p.default_value || ''
}))
});
}
// Use flow metadata if available, otherwise use basic info
const flowYaml: FlowsYamlFlow = {
idn: flowIdn,
title: flowMeta?.title || 'Unknown Flow',
description: flowMeta?.description || null,
default_runner_type: flowMeta?.default_runner_type || 'guidance',
default_provider_idn: flowMeta?.default_model?.provider_idn || 'openai',
default_model_idn: flowMeta?.default_model?.model_idn || 'gpt-4',
skills,
events: flowMeta?.events?.map(event => ({
title: event.description,
idn: event.idn,
skill_selector: event.skill_selector,
skill_idn: event.skill_idn || null,
state_idn: event.state_idn || null,
integration_idn: event.integration_idn || null,
connector_idn: event.connector_idn || null,
interrupt_mode: event.interrupt_mode
})) || [],
state_fields: flowMeta?.state_fields?.map(state => ({
title: state.title,
idn: state.idn,
default_value: state.default_value || null,
scope: state.scope
})) || []
};
agentFlows.push(flowYaml);
}
if (agentFlows.length > 0) {
// Load agent metadata for description
const agentMetaPath = agentMetadataPath(customerIdn, projectIdn, agentIdn);
let agentDescription: string | null = null;
try {
if (await fs.pathExists(agentMetaPath)) {
const agentMetaContent = await fs.readFile(agentMetaPath, 'utf8');
const agentMeta = yaml.load(agentMetaContent) as AgentMetadata;
agentDescription = agentMeta.description || null;
}
} catch (e) {
if (verbose) console.log(` ⚠️ Could not load agent metadata: ${agentMetaPath}`);
}
flowsData.flows.push({
agent_idn: agentIdn,
agent_description: agentDescription,
agent_flows: agentFlows
});
}
}
}
// Generate flows.yaml content
const flowsYamlContent = yaml.dump(flowsData, {
indent: 2,
quotingType: '"',
forceQuotes: false,
lineWidth: 120,
noRefs: true,
sortKeys: false,
flowLevel: -1
});
// Save flows.yaml
const flowsFilePath = flowsYamlPath(customerIdn);
await writeFileSafe(flowsFilePath, flowsYamlContent);
if (verbose) console.log(`✓ Generated flows.yaml with ${flowsData.flows.length} agents`);
// Return content for hash calculation
return flowsYamlContent;
}