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.
110 lines (96 loc) • 4.07 kB
text/typescript
/**
* Customer attributes synchronization module
*/
import { getCustomerAttributes } from '../api.js';
import {
writeFileSafe,
customerAttributesPath,
customerAttributesMapPath,
customerAttributesBackupPath
} from '../fsutil.js';
import yaml from 'js-yaml';
import type { AxiosInstance } from 'axios';
import type { CustomerConfig } from '../types.js';
/**
* Save customer attributes to YAML format and return content for hashing
*/
export async function saveCustomerAttributes(
client: AxiosInstance,
customer: CustomerConfig,
verbose: boolean = false
): Promise<string> {
if (verbose) console.log(`🔍 Fetching customer attributes for ${customer.idn}...`);
try {
const response = await getCustomerAttributes(client, true); // Include hidden attributes
// API returns { groups: [...], attributes: [...] }
// We only want the attributes array in the expected format
const attributes = response.attributes || response;
if (verbose) console.log(`📦 Found ${Array.isArray(attributes) ? attributes.length : 'invalid'} attributes`);
// Create ID mapping for push operations (separate from YAML)
const idMapping: Record<string, string> = {};
// Transform attributes to match reference format exactly (no ID fields)
const cleanAttributes = Array.isArray(attributes) ? attributes.map(attr => {
// Store ID mapping for push operations
if (attr.id) {
idMapping[attr.idn] = attr.id;
}
// Special handling for complex JSON string values
let processedValue = attr.value;
if (typeof attr.value === 'string' && attr.value.startsWith('[{') && attr.value.endsWith('}]')) {
try {
// Parse and reformat JSON for better readability
const parsed = JSON.parse(attr.value);
processedValue = JSON.stringify(parsed, null, 0); // No extra spacing, but valid JSON
} catch (e) {
// Keep original if parsing fails
processedValue = attr.value;
}
}
const cleanAttr: any = {
idn: attr.idn,
value: processedValue,
title: attr.title || "",
description: attr.description || "",
group: attr.group || "",
is_hidden: attr.is_hidden,
possible_values: attr.possible_values || [],
value_type: `__ENUM_PLACEHOLDER_${attr.value_type}__`
};
return cleanAttr;
}) : [];
const attributesYaml = {
attributes: cleanAttributes
};
// Configure YAML output to match reference format exactly
let yamlContent = yaml.dump(attributesYaml, {
indent: 2,
quotingType: '"',
forceQuotes: false,
lineWidth: 80, // Wrap long lines to match reference format
noRefs: true,
sortKeys: false,
flowLevel: -1, // Never use flow syntax
styles: {
'!!str': 'folded' // Use folded style for better line wrapping of long strings
}
});
// Post-process to fix enum format and improve JSON string formatting
yamlContent = yamlContent.replace(/__ENUM_PLACEHOLDER_(\w+)__/g, '!enum "AttributeValueTypes.$1"');
// Fix JSON string formatting to match reference (remove escape characters)
yamlContent = yamlContent.replace(/\\"/g, '"');
// Save all files: attributes.yaml, ID mapping, and backup for diff tracking
await writeFileSafe(customerAttributesPath(customer.idn), yamlContent);
await writeFileSafe(customerAttributesMapPath(customer.idn), JSON.stringify(idMapping, null, 2));
await writeFileSafe(customerAttributesBackupPath(customer.idn), yamlContent);
if (verbose) {
console.log(`✓ Saved customer attributes to ${customerAttributesPath(customer.idn)}`);
console.log(`✓ Saved attribute ID mapping to ${customerAttributesMapPath(customer.idn)}`);
console.log(`✓ Created attributes backup for diff tracking`);
}
// Return content for hash calculation
return yamlContent;
} catch (error) {
console.error(`❌ Failed to save customer attributes for ${customer.idn}:`, error);
throw error;
}
}