newo 1.9.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +116 -0
- package/README.md +68 -20
- package/dist/cli/commands/conversations.d.ts +3 -0
- package/dist/cli/commands/conversations.js +38 -0
- package/dist/cli/commands/help.d.ts +5 -0
- package/dist/cli/commands/help.js +50 -0
- package/dist/cli/commands/import-akb.d.ts +3 -0
- package/dist/cli/commands/import-akb.js +62 -0
- package/dist/cli/commands/list-customers.d.ts +3 -0
- package/dist/cli/commands/list-customers.js +13 -0
- package/dist/cli/commands/meta.d.ts +3 -0
- package/dist/cli/commands/meta.js +19 -0
- package/dist/cli/commands/pull-attributes.d.ts +3 -0
- package/dist/cli/commands/pull-attributes.js +16 -0
- package/dist/cli/commands/pull.d.ts +3 -0
- package/dist/cli/commands/pull.js +34 -0
- package/dist/cli/commands/push.d.ts +3 -0
- package/dist/cli/commands/push.js +39 -0
- package/dist/cli/commands/status.d.ts +3 -0
- package/dist/cli/commands/status.js +22 -0
- package/dist/cli/customer-selection.d.ts +23 -0
- package/dist/cli/customer-selection.js +110 -0
- package/dist/cli/errors.d.ts +9 -0
- package/dist/cli/errors.js +111 -0
- package/dist/cli.js +66 -463
- package/dist/fsutil.js +1 -1
- package/dist/sync/attributes.d.ts +7 -0
- package/dist/sync/attributes.js +90 -0
- package/dist/sync/conversations.d.ts +7 -0
- package/dist/sync/conversations.js +218 -0
- package/dist/sync/metadata.d.ts +8 -0
- package/dist/sync/metadata.js +124 -0
- package/dist/sync/projects.d.ts +13 -0
- package/dist/sync/projects.js +283 -0
- package/dist/sync/push.d.ts +7 -0
- package/dist/sync/push.js +171 -0
- package/dist/sync/skill-files.d.ts +42 -0
- package/dist/sync/skill-files.js +121 -0
- package/dist/sync/status.d.ts +6 -0
- package/dist/sync/status.js +247 -0
- package/dist/sync.d.ts +10 -8
- package/dist/sync.js +12 -1226
- package/dist/types.d.ts +0 -1
- package/package.json +2 -2
- package/src/cli/commands/conversations.ts +47 -0
- package/src/cli/commands/help.ts +50 -0
- package/src/cli/commands/import-akb.ts +71 -0
- package/src/cli/commands/list-customers.ts +14 -0
- package/src/cli/commands/meta.ts +26 -0
- package/src/cli/commands/pull-attributes.ts +23 -0
- package/src/cli/commands/pull.ts +43 -0
- package/src/cli/commands/push.ts +47 -0
- package/src/cli/commands/status.ts +30 -0
- package/src/cli/customer-selection.ts +135 -0
- package/src/cli/errors.ts +111 -0
- package/src/cli.ts +77 -471
- package/src/fsutil.ts +1 -1
- package/src/sync/attributes.ts +110 -0
- package/src/sync/conversations.ts +257 -0
- package/src/sync/metadata.ts +153 -0
- package/src/sync/projects.ts +359 -0
- package/src/sync/push.ts +200 -0
- package/src/sync/skill-files.ts +176 -0
- package/src/sync/status.ts +277 -0
- package/src/sync.ts +14 -1418
- package/src/types.ts +0 -1
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Customer attributes synchronization module
|
|
3
|
+
*/
|
|
4
|
+
import { getCustomerAttributes } from '../api.js';
|
|
5
|
+
import { writeFileSafe, customerAttributesPath, customerAttributesMapPath, customerAttributesBackupPath } from '../fsutil.js';
|
|
6
|
+
import yaml from 'js-yaml';
|
|
7
|
+
/**
|
|
8
|
+
* Save customer attributes to YAML format and return content for hashing
|
|
9
|
+
*/
|
|
10
|
+
export async function saveCustomerAttributes(client, customer, verbose = false) {
|
|
11
|
+
if (verbose)
|
|
12
|
+
console.log(`🔍 Fetching customer attributes for ${customer.idn}...`);
|
|
13
|
+
try {
|
|
14
|
+
const response = await getCustomerAttributes(client, true); // Include hidden attributes
|
|
15
|
+
// API returns { groups: [...], attributes: [...] }
|
|
16
|
+
// We only want the attributes array in the expected format
|
|
17
|
+
const attributes = response.attributes || response;
|
|
18
|
+
if (verbose)
|
|
19
|
+
console.log(`📦 Found ${Array.isArray(attributes) ? attributes.length : 'invalid'} attributes`);
|
|
20
|
+
// Create ID mapping for push operations (separate from YAML)
|
|
21
|
+
const idMapping = {};
|
|
22
|
+
// Transform attributes to match reference format exactly (no ID fields)
|
|
23
|
+
const cleanAttributes = Array.isArray(attributes) ? attributes.map(attr => {
|
|
24
|
+
// Store ID mapping for push operations
|
|
25
|
+
if (attr.id) {
|
|
26
|
+
idMapping[attr.idn] = attr.id;
|
|
27
|
+
}
|
|
28
|
+
// Special handling for complex JSON string values
|
|
29
|
+
let processedValue = attr.value;
|
|
30
|
+
if (typeof attr.value === 'string' && attr.value.startsWith('[{') && attr.value.endsWith('}]')) {
|
|
31
|
+
try {
|
|
32
|
+
// Parse and reformat JSON for better readability
|
|
33
|
+
const parsed = JSON.parse(attr.value);
|
|
34
|
+
processedValue = JSON.stringify(parsed, null, 0); // No extra spacing, but valid JSON
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
// Keep original if parsing fails
|
|
38
|
+
processedValue = attr.value;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const cleanAttr = {
|
|
42
|
+
idn: attr.idn,
|
|
43
|
+
value: processedValue,
|
|
44
|
+
title: attr.title || "",
|
|
45
|
+
description: attr.description || "",
|
|
46
|
+
group: attr.group || "",
|
|
47
|
+
is_hidden: attr.is_hidden,
|
|
48
|
+
possible_values: attr.possible_values || [],
|
|
49
|
+
value_type: `__ENUM_PLACEHOLDER_${attr.value_type}__`
|
|
50
|
+
};
|
|
51
|
+
return cleanAttr;
|
|
52
|
+
}) : [];
|
|
53
|
+
const attributesYaml = {
|
|
54
|
+
attributes: cleanAttributes
|
|
55
|
+
};
|
|
56
|
+
// Configure YAML output to match reference format exactly
|
|
57
|
+
let yamlContent = yaml.dump(attributesYaml, {
|
|
58
|
+
indent: 2,
|
|
59
|
+
quotingType: '"',
|
|
60
|
+
forceQuotes: false,
|
|
61
|
+
lineWidth: 80, // Wrap long lines to match reference format
|
|
62
|
+
noRefs: true,
|
|
63
|
+
sortKeys: false,
|
|
64
|
+
flowLevel: -1, // Never use flow syntax
|
|
65
|
+
styles: {
|
|
66
|
+
'!!str': 'folded' // Use folded style for better line wrapping of long strings
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
// Post-process to fix enum format and improve JSON string formatting
|
|
70
|
+
yamlContent = yamlContent.replace(/__ENUM_PLACEHOLDER_(\w+)__/g, '!enum "AttributeValueTypes.$1"');
|
|
71
|
+
// Fix JSON string formatting to match reference (remove escape characters)
|
|
72
|
+
yamlContent = yamlContent.replace(/\\"/g, '"');
|
|
73
|
+
// Save all files: attributes.yaml, ID mapping, and backup for diff tracking
|
|
74
|
+
await writeFileSafe(customerAttributesPath(customer.idn), yamlContent);
|
|
75
|
+
await writeFileSafe(customerAttributesMapPath(customer.idn), JSON.stringify(idMapping, null, 2));
|
|
76
|
+
await writeFileSafe(customerAttributesBackupPath(customer.idn), yamlContent);
|
|
77
|
+
if (verbose) {
|
|
78
|
+
console.log(`✓ Saved customer attributes to ${customerAttributesPath(customer.idn)}`);
|
|
79
|
+
console.log(`✓ Saved attribute ID mapping to ${customerAttributesMapPath(customer.idn)}`);
|
|
80
|
+
console.log(`✓ Created attributes backup for diff tracking`);
|
|
81
|
+
}
|
|
82
|
+
// Return content for hash calculation
|
|
83
|
+
return yamlContent;
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
console.error(`❌ Failed to save customer attributes for ${customer.idn}:`, error);
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=attributes.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AxiosInstance } from 'axios';
|
|
2
|
+
import type { CustomerConfig, ConversationOptions } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Pull conversations for a customer and save to YAML
|
|
5
|
+
*/
|
|
6
|
+
export declare function pullConversations(client: AxiosInstance, customer: CustomerConfig, options?: ConversationOptions, verbose?: boolean): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=conversations.d.ts.map
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversations synchronization module
|
|
3
|
+
*/
|
|
4
|
+
import { listUserPersonas, getChatHistory } from '../api.js';
|
|
5
|
+
import { writeFileSafe } from '../fsutil.js';
|
|
6
|
+
import yaml from 'js-yaml';
|
|
7
|
+
import pLimit from 'p-limit';
|
|
8
|
+
// Concurrency limit for API calls
|
|
9
|
+
const concurrencyLimit = pLimit(5);
|
|
10
|
+
/**
|
|
11
|
+
* Pull conversations for a customer and save to YAML
|
|
12
|
+
*/
|
|
13
|
+
export async function pullConversations(client, customer, options = {}, verbose = false) {
|
|
14
|
+
if (verbose)
|
|
15
|
+
console.log(`💬 Fetching conversations for customer ${customer.idn}...`);
|
|
16
|
+
try {
|
|
17
|
+
// Get all user personas with pagination
|
|
18
|
+
const allPersonas = [];
|
|
19
|
+
let page = 1;
|
|
20
|
+
const perPage = 50;
|
|
21
|
+
let hasMore = true;
|
|
22
|
+
while (hasMore) {
|
|
23
|
+
const response = await listUserPersonas(client, page, perPage);
|
|
24
|
+
allPersonas.push(...response.items);
|
|
25
|
+
if (verbose)
|
|
26
|
+
console.log(`📋 Page ${page}: Found ${response.items.length} personas (${allPersonas.length}/${response.metadata.total} total)`);
|
|
27
|
+
hasMore = response.items.length === perPage && allPersonas.length < response.metadata.total;
|
|
28
|
+
page++;
|
|
29
|
+
}
|
|
30
|
+
if (options.maxPersonas && allPersonas.length > options.maxPersonas) {
|
|
31
|
+
allPersonas.splice(options.maxPersonas);
|
|
32
|
+
if (verbose)
|
|
33
|
+
console.log(`⚠️ Limited to ${options.maxPersonas} personas as requested`);
|
|
34
|
+
}
|
|
35
|
+
if (verbose)
|
|
36
|
+
console.log(`👥 Processing ${allPersonas.length} personas...`);
|
|
37
|
+
// Process personas concurrently with limited concurrency
|
|
38
|
+
const processedPersonas = [];
|
|
39
|
+
await Promise.all(allPersonas.map(persona => concurrencyLimit(async () => {
|
|
40
|
+
try {
|
|
41
|
+
// Extract phone number from actors
|
|
42
|
+
const phoneActor = persona.actors.find(actor => actor.integration_idn === 'newo_voice' &&
|
|
43
|
+
actor.connector_idn === 'newo_voice_connector' &&
|
|
44
|
+
actor.contact_information?.startsWith('+'));
|
|
45
|
+
const phone = phoneActor?.contact_information || null;
|
|
46
|
+
// Get acts for this persona
|
|
47
|
+
const allActs = [];
|
|
48
|
+
let actPage = 1;
|
|
49
|
+
const actsPerPage = 100; // Higher limit for acts
|
|
50
|
+
let hasMoreActs = true;
|
|
51
|
+
// Get user actor IDs from persona actors first
|
|
52
|
+
const userActors = persona.actors.filter(actor => actor.integration_idn === 'newo_voice' &&
|
|
53
|
+
actor.connector_idn === 'newo_voice_connector');
|
|
54
|
+
if (userActors.length === 0) {
|
|
55
|
+
if (verbose)
|
|
56
|
+
console.log(` 👤 ${persona.name}: No voice actors found, skipping`);
|
|
57
|
+
// No voice actors, can't get chat history - add persona with empty acts
|
|
58
|
+
processedPersonas.push({
|
|
59
|
+
id: persona.id,
|
|
60
|
+
name: persona.name,
|
|
61
|
+
phone,
|
|
62
|
+
act_count: persona.act_count,
|
|
63
|
+
acts: []
|
|
64
|
+
});
|
|
65
|
+
if (verbose)
|
|
66
|
+
console.log(` ✓ Processed ${persona.name}: 0 acts (no voice actors)`);
|
|
67
|
+
return; // Return from the concurrency function
|
|
68
|
+
}
|
|
69
|
+
// Safety mechanism to prevent infinite loops
|
|
70
|
+
const maxPages = 50; // Limit to 50 pages (5000 acts max per persona)
|
|
71
|
+
while (hasMoreActs && actPage <= maxPages) {
|
|
72
|
+
try {
|
|
73
|
+
const chatHistoryParams = {
|
|
74
|
+
user_actor_id: userActors[0].id,
|
|
75
|
+
page: actPage,
|
|
76
|
+
per: actsPerPage
|
|
77
|
+
};
|
|
78
|
+
if (verbose)
|
|
79
|
+
console.log(` 📄 ${persona.name}: Fetching page ${actPage}...`);
|
|
80
|
+
const chatResponse = await getChatHistory(client, chatHistoryParams);
|
|
81
|
+
if (chatResponse.items && chatResponse.items.length > 0) {
|
|
82
|
+
// Convert chat history format to acts format - create minimal ConversationAct objects
|
|
83
|
+
const convertedActs = chatResponse.items.map((item) => ({
|
|
84
|
+
id: item.id || `chat_${Math.random()}`,
|
|
85
|
+
command_act_id: null,
|
|
86
|
+
external_event_id: item.external_event_id || 'chat_history',
|
|
87
|
+
arguments: [],
|
|
88
|
+
reference_idn: (item.is_agent === true) ? 'agent_message' : 'user_message',
|
|
89
|
+
runtime_context_id: item.runtime_context_id || 'chat_history',
|
|
90
|
+
source_text: item.payload?.text || item.message || item.content || item.text || '',
|
|
91
|
+
original_text: item.payload?.text || item.message || item.content || item.text || '',
|
|
92
|
+
datetime: item.datetime || item.created_at || item.timestamp || new Date().toISOString(),
|
|
93
|
+
user_actor_id: userActors[0].id,
|
|
94
|
+
agent_actor_id: null,
|
|
95
|
+
user_persona_id: persona.id,
|
|
96
|
+
user_persona_name: persona.name,
|
|
97
|
+
agent_persona_id: item.agent_persona_id || 'unknown',
|
|
98
|
+
external_id: item.external_id || null,
|
|
99
|
+
integration_idn: 'newo_voice',
|
|
100
|
+
connector_idn: 'newo_voice_connector',
|
|
101
|
+
to_integration_idn: null,
|
|
102
|
+
to_connector_idn: null,
|
|
103
|
+
is_agent: Boolean(item.is_agent === true),
|
|
104
|
+
project_idn: null,
|
|
105
|
+
flow_idn: item.flow_idn || 'unknown',
|
|
106
|
+
skill_idn: item.skill_idn || 'unknown',
|
|
107
|
+
session_id: item.session_id || 'unknown',
|
|
108
|
+
recordings: item.recordings || [],
|
|
109
|
+
contact_information: item.contact_information || null
|
|
110
|
+
}));
|
|
111
|
+
allActs.push(...convertedActs);
|
|
112
|
+
if (verbose && convertedActs.length > 0) {
|
|
113
|
+
console.log(` 👤 ${persona.name}: Chat History - ${convertedActs.length} messages (${allActs.length} total)`);
|
|
114
|
+
}
|
|
115
|
+
// Check if we should continue paginating
|
|
116
|
+
const hasMetadata = chatResponse.metadata?.total !== undefined;
|
|
117
|
+
const currentTotal = chatResponse.metadata?.total || 0;
|
|
118
|
+
hasMoreActs = chatResponse.items.length === actsPerPage &&
|
|
119
|
+
hasMetadata &&
|
|
120
|
+
allActs.length < currentTotal;
|
|
121
|
+
actPage++;
|
|
122
|
+
if (verbose)
|
|
123
|
+
console.log(` 📊 ${persona.name}: Page ${actPage - 1} done, ${allActs.length}/${currentTotal} total acts`);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
// No more items
|
|
127
|
+
hasMoreActs = false;
|
|
128
|
+
if (verbose)
|
|
129
|
+
console.log(` 📊 ${persona.name}: No more chat history items`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (chatError) {
|
|
133
|
+
if (verbose)
|
|
134
|
+
console.log(` ⚠️ Chat history failed for ${persona.name}: ${chatError instanceof Error ? chatError.message : String(chatError)}`);
|
|
135
|
+
hasMoreActs = false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (actPage > maxPages) {
|
|
139
|
+
if (verbose)
|
|
140
|
+
console.log(` ⚠️ ${persona.name}: Reached max pages limit (${maxPages}), stopping pagination`);
|
|
141
|
+
}
|
|
142
|
+
// Sort acts by datetime ascending (chronological order)
|
|
143
|
+
allActs.sort((a, b) => new Date(a.datetime).getTime() - new Date(b.datetime).getTime());
|
|
144
|
+
// Process acts into simplified format - exclude redundant fields
|
|
145
|
+
const processedActs = allActs.map(act => {
|
|
146
|
+
const processedAct = {
|
|
147
|
+
datetime: act.datetime,
|
|
148
|
+
type: act.reference_idn,
|
|
149
|
+
message: act.source_text
|
|
150
|
+
};
|
|
151
|
+
// Only include non-redundant fields
|
|
152
|
+
if (act.contact_information) {
|
|
153
|
+
processedAct.contact_information = act.contact_information;
|
|
154
|
+
}
|
|
155
|
+
if (act.flow_idn && act.flow_idn !== 'unknown') {
|
|
156
|
+
processedAct.flow_idn = act.flow_idn;
|
|
157
|
+
}
|
|
158
|
+
if (act.skill_idn && act.skill_idn !== 'unknown') {
|
|
159
|
+
processedAct.skill_idn = act.skill_idn;
|
|
160
|
+
}
|
|
161
|
+
if (act.session_id && act.session_id !== 'unknown') {
|
|
162
|
+
processedAct.session_id = act.session_id;
|
|
163
|
+
}
|
|
164
|
+
return processedAct;
|
|
165
|
+
});
|
|
166
|
+
processedPersonas.push({
|
|
167
|
+
id: persona.id,
|
|
168
|
+
name: persona.name,
|
|
169
|
+
phone,
|
|
170
|
+
act_count: persona.act_count,
|
|
171
|
+
acts: processedActs
|
|
172
|
+
});
|
|
173
|
+
if (verbose)
|
|
174
|
+
console.log(` ✓ Processed ${persona.name}: ${processedActs.length} acts`);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
console.error(`❌ Failed to process persona ${persona.name}:`, error);
|
|
178
|
+
// Continue with other personas
|
|
179
|
+
}
|
|
180
|
+
})));
|
|
181
|
+
// Sort personas by most recent act time (descending) - use latest act from acts array
|
|
182
|
+
processedPersonas.sort((a, b) => {
|
|
183
|
+
const aLatestTime = a.acts.length > 0 ? a.acts[a.acts.length - 1].datetime : '1970-01-01T00:00:00.000Z';
|
|
184
|
+
const bLatestTime = b.acts.length > 0 ? b.acts[b.acts.length - 1].datetime : '1970-01-01T00:00:00.000Z';
|
|
185
|
+
return new Date(bLatestTime).getTime() - new Date(aLatestTime).getTime();
|
|
186
|
+
});
|
|
187
|
+
// Calculate totals
|
|
188
|
+
const totalActs = processedPersonas.reduce((sum, persona) => sum + persona.acts.length, 0);
|
|
189
|
+
// Create final conversations data
|
|
190
|
+
const conversationsData = {
|
|
191
|
+
personas: processedPersonas,
|
|
192
|
+
total_personas: processedPersonas.length,
|
|
193
|
+
total_acts: totalActs,
|
|
194
|
+
generated_at: new Date().toISOString()
|
|
195
|
+
};
|
|
196
|
+
// Save to YAML file
|
|
197
|
+
const conversationsPath = `newo_customers/${customer.idn}/conversations.yaml`;
|
|
198
|
+
const yamlContent = yaml.dump(conversationsData, {
|
|
199
|
+
indent: 2,
|
|
200
|
+
quotingType: '"',
|
|
201
|
+
forceQuotes: false,
|
|
202
|
+
lineWidth: 120,
|
|
203
|
+
noRefs: true,
|
|
204
|
+
sortKeys: false,
|
|
205
|
+
flowLevel: -1
|
|
206
|
+
});
|
|
207
|
+
await writeFileSafe(conversationsPath, yamlContent);
|
|
208
|
+
if (verbose) {
|
|
209
|
+
console.log(`✓ Saved conversations to ${conversationsPath}`);
|
|
210
|
+
console.log(`📊 Summary: ${processedPersonas.length} personas, ${totalActs} total acts`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
console.error(`❌ Failed to pull conversations for ${customer.idn}:`, error);
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=conversations.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ProjectData, ProjectMap } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generate flows.yaml file from project data
|
|
4
|
+
*/
|
|
5
|
+
export declare function generateFlowsYaml(projectMap: ProjectMap | {
|
|
6
|
+
[key: string]: ProjectData;
|
|
7
|
+
}, customerIdn: string, verbose?: boolean): Promise<string>;
|
|
8
|
+
//# sourceMappingURL=metadata.d.ts.map
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metadata and flows.yaml generation operations
|
|
3
|
+
*/
|
|
4
|
+
import { writeFileSafe, flowMetadataPath, agentMetadataPath, flowsYamlPath } from '../fsutil.js';
|
|
5
|
+
import fs from 'fs-extra';
|
|
6
|
+
import yaml from 'js-yaml';
|
|
7
|
+
/**
|
|
8
|
+
* Generate flows.yaml file from project data
|
|
9
|
+
*/
|
|
10
|
+
export async function generateFlowsYaml(projectMap, customerIdn, verbose = false) {
|
|
11
|
+
if (verbose)
|
|
12
|
+
console.log(`📊 Generating flows.yaml for customer ${customerIdn}...`);
|
|
13
|
+
const flowsData = {
|
|
14
|
+
flows: []
|
|
15
|
+
};
|
|
16
|
+
// Handle both formats
|
|
17
|
+
const projects = 'projects' in projectMap ? projectMap.projects : projectMap;
|
|
18
|
+
for (const [projectIdn, projectData] of Object.entries(projects)) {
|
|
19
|
+
if (verbose && projectIdn)
|
|
20
|
+
console.log(` 📁 Processing project: ${projectIdn}`);
|
|
21
|
+
for (const [agentIdn, agentData] of Object.entries(projectData.agents)) {
|
|
22
|
+
if (verbose)
|
|
23
|
+
console.log(` 📁 Processing agent: ${agentIdn}`);
|
|
24
|
+
const agentFlows = [];
|
|
25
|
+
for (const [flowIdn, flowData] of Object.entries(agentData.flows)) {
|
|
26
|
+
if (verbose)
|
|
27
|
+
console.log(` 📁 Processing flow: ${flowIdn}`);
|
|
28
|
+
// Load flow metadata to get comprehensive flow information
|
|
29
|
+
const flowMetaPath = flowMetadataPath(customerIdn, projectIdn, agentIdn, flowIdn);
|
|
30
|
+
let flowMeta = null;
|
|
31
|
+
try {
|
|
32
|
+
if (await fs.pathExists(flowMetaPath)) {
|
|
33
|
+
const flowMetaContent = await fs.readFile(flowMetaPath, 'utf8');
|
|
34
|
+
flowMeta = yaml.load(flowMetaContent);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
if (verbose)
|
|
39
|
+
console.log(` ⚠️ Could not load flow metadata: ${flowMetaPath}`);
|
|
40
|
+
}
|
|
41
|
+
const skills = [];
|
|
42
|
+
for (const [, skillMeta] of Object.entries(flowData.skills)) {
|
|
43
|
+
// Note: We don't need to load script content since prompt_script is excluded from flows.yaml
|
|
44
|
+
skills.push({
|
|
45
|
+
idn: skillMeta.idn,
|
|
46
|
+
title: skillMeta.title,
|
|
47
|
+
runner_type: skillMeta.runner_type,
|
|
48
|
+
model: skillMeta.model,
|
|
49
|
+
parameters: skillMeta.parameters.map((p) => ({
|
|
50
|
+
name: p.name,
|
|
51
|
+
default_value: p.default_value || ''
|
|
52
|
+
}))
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
// Use flow metadata if available, otherwise use basic info
|
|
56
|
+
const flowYaml = {
|
|
57
|
+
idn: flowIdn,
|
|
58
|
+
title: flowMeta?.title || 'Unknown Flow',
|
|
59
|
+
description: flowMeta?.description || null,
|
|
60
|
+
default_runner_type: flowMeta?.default_runner_type || 'guidance',
|
|
61
|
+
default_provider_idn: flowMeta?.default_model?.provider_idn || 'openai',
|
|
62
|
+
default_model_idn: flowMeta?.default_model?.model_idn || 'gpt-4',
|
|
63
|
+
skills,
|
|
64
|
+
events: flowMeta?.events?.map(event => ({
|
|
65
|
+
title: event.description,
|
|
66
|
+
idn: event.idn,
|
|
67
|
+
skill_selector: event.skill_selector,
|
|
68
|
+
skill_idn: event.skill_idn || null,
|
|
69
|
+
state_idn: event.state_idn || null,
|
|
70
|
+
integration_idn: event.integration_idn || null,
|
|
71
|
+
connector_idn: event.connector_idn || null,
|
|
72
|
+
interrupt_mode: event.interrupt_mode
|
|
73
|
+
})) || [],
|
|
74
|
+
state_fields: flowMeta?.state_fields?.map(state => ({
|
|
75
|
+
title: state.title,
|
|
76
|
+
idn: state.idn,
|
|
77
|
+
default_value: state.default_value || null,
|
|
78
|
+
scope: state.scope
|
|
79
|
+
})) || []
|
|
80
|
+
};
|
|
81
|
+
agentFlows.push(flowYaml);
|
|
82
|
+
}
|
|
83
|
+
if (agentFlows.length > 0) {
|
|
84
|
+
// Load agent metadata for description
|
|
85
|
+
const agentMetaPath = agentMetadataPath(customerIdn, projectIdn, agentIdn);
|
|
86
|
+
let agentDescription = null;
|
|
87
|
+
try {
|
|
88
|
+
if (await fs.pathExists(agentMetaPath)) {
|
|
89
|
+
const agentMetaContent = await fs.readFile(agentMetaPath, 'utf8');
|
|
90
|
+
const agentMeta = yaml.load(agentMetaContent);
|
|
91
|
+
agentDescription = agentMeta.description || null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
if (verbose)
|
|
96
|
+
console.log(` ⚠️ Could not load agent metadata: ${agentMetaPath}`);
|
|
97
|
+
}
|
|
98
|
+
flowsData.flows.push({
|
|
99
|
+
agent_idn: agentIdn,
|
|
100
|
+
agent_description: agentDescription,
|
|
101
|
+
agent_flows: agentFlows
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Generate flows.yaml content
|
|
107
|
+
const flowsYamlContent = yaml.dump(flowsData, {
|
|
108
|
+
indent: 2,
|
|
109
|
+
quotingType: '"',
|
|
110
|
+
forceQuotes: false,
|
|
111
|
+
lineWidth: 120,
|
|
112
|
+
noRefs: true,
|
|
113
|
+
sortKeys: false,
|
|
114
|
+
flowLevel: -1
|
|
115
|
+
});
|
|
116
|
+
// Save flows.yaml
|
|
117
|
+
const flowsFilePath = flowsYamlPath(customerIdn);
|
|
118
|
+
await writeFileSafe(flowsFilePath, flowsYamlContent);
|
|
119
|
+
if (verbose)
|
|
120
|
+
console.log(`✓ Generated flows.yaml with ${flowsData.flows.length} agents`);
|
|
121
|
+
// Return content for hash calculation
|
|
122
|
+
return flowsYamlContent;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=metadata.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AxiosInstance } from 'axios';
|
|
2
|
+
import type { ProjectMap, LegacyProjectMap, CustomerConfig } from '../types.js';
|
|
3
|
+
export declare function isProjectMap(x: unknown): x is ProjectMap;
|
|
4
|
+
export declare function isLegacyProjectMap(x: unknown): x is LegacyProjectMap;
|
|
5
|
+
/**
|
|
6
|
+
* Pull a single project and all its data
|
|
7
|
+
*/
|
|
8
|
+
export declare function pullSingleProject(client: AxiosInstance, customer: CustomerConfig, projectId: string | null, verbose?: boolean, silentOverwrite?: boolean): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Pull all projects for a customer
|
|
11
|
+
*/
|
|
12
|
+
export declare function pullAll(client: AxiosInstance, customer: CustomerConfig, projectId?: string | null, verbose?: boolean, silentOverwrite?: boolean): Promise<void>;
|
|
13
|
+
//# sourceMappingURL=projects.d.ts.map
|