newo 3.2.0 → 3.3.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.
@@ -0,0 +1,590 @@
1
+ /**
2
+ * Account migration operations
3
+ * Migrates complete account from source to destination
4
+ */
5
+ import { listProjects, listAgents, createProject, createAgent, createFlow, createSkill, createFlowEvent, createFlowState, getCustomerAttributes, createCustomerAttribute, updateCustomerAttribute, getProjectAttributes, createProjectAttribute, updateProjectAttribute, searchPersonas, getAkbTopics, createPersona, importAkbArticle, listIntegrations, listConnectors, createConnector, listFlowSkills, updateSkill } from '../api.js';
6
+ import { pullAll } from './projects.js';
7
+ import { pullIntegrations } from './integrations.js';
8
+ import { customerDir, customerProjectsDir } from '../fsutil.js';
9
+ import fs from 'fs-extra';
10
+ import yaml from 'js-yaml';
11
+ import path from 'path';
12
+ /**
13
+ * Migrate complete account from source to destination
14
+ */
15
+ export async function migrateAccount(options) {
16
+ const { sourceCustomer, destCustomer, sourceClient, destClient, verbose } = options;
17
+ const result = {
18
+ success: false,
19
+ projectsCreated: 0,
20
+ agentsCreated: 0,
21
+ flowsCreated: 0,
22
+ skillsCreated: 0,
23
+ attributesMigrated: 0,
24
+ personasCreated: 0,
25
+ articlesImported: 0,
26
+ connectorsCreated: 0,
27
+ webhooksCreated: 0,
28
+ errors: []
29
+ };
30
+ try {
31
+ console.log('\nšŸ”„ Starting account migration...');
32
+ console.log(`Source: ${sourceCustomer.idn}`);
33
+ console.log(`Destination: ${destCustomer.idn}\n`);
34
+ // Step 1: Pull source data
35
+ console.log('šŸ“„ Step 1: Pulling source data...');
36
+ await pullAll(sourceClient, sourceCustomer, null, verbose, true);
37
+ await pullIntegrations(sourceClient, customerDir(sourceCustomer.idn), verbose);
38
+ console.log(' āœ… Source data pulled\n');
39
+ // Step 2: Create project structure
40
+ console.log('šŸ—ļø Step 2: Creating project structure...');
41
+ const projectCounts = await migrateProjectStructure(sourceClient, destClient, sourceCustomer, destCustomer, verbose);
42
+ result.projectsCreated = projectCounts.projects;
43
+ result.agentsCreated = projectCounts.agents;
44
+ result.flowsCreated = projectCounts.flows;
45
+ result.skillsCreated = projectCounts.skills;
46
+ console.log(` āœ… Created: ${result.projectsCreated} projects, ${result.agentsCreated} agents, ${result.flowsCreated} flows, ${result.skillsCreated} skills\n`);
47
+ // Step 3: Migrate attributes
48
+ console.log('šŸ“Š Step 3: Migrating attributes...');
49
+ result.attributesMigrated = await migrateAttributes(sourceClient, destClient, sourceCustomer, destCustomer, verbose);
50
+ console.log(` āœ… Migrated: ${result.attributesMigrated} attributes\n`);
51
+ // Step 4: Migrate AKB
52
+ console.log('šŸ“š Step 4: Migrating AKB...');
53
+ const akbCounts = await migrateAKB(sourceClient, destClient, verbose);
54
+ result.personasCreated = akbCounts.personas;
55
+ result.articlesImported = akbCounts.articles;
56
+ console.log(` āœ… Migrated: ${result.personasCreated} personas, ${result.articlesImported} articles\n`);
57
+ // Step 5: Migrate integrations
58
+ console.log('šŸ”Œ Step 5: Migrating integrations...');
59
+ result.connectorsCreated = await migrateIntegrationConnectors(sourceClient, destClient, verbose);
60
+ console.log(` āœ… Created: ${result.connectorsCreated} connectors\n`);
61
+ // Step 6: Copy files
62
+ console.log('šŸ“ Step 6: Copying files...');
63
+ await copyAccountFiles(sourceCustomer.idn, destCustomer.idn);
64
+ console.log(' āœ… Files copied\n');
65
+ // Step 7: Build map from API
66
+ console.log('šŸ“ Step 7: Building destination mappings...');
67
+ await buildMapFromAPI(destClient, destCustomer, verbose);
68
+ console.log(' āœ… Mappings built\n');
69
+ // Step 8: Push skill content
70
+ console.log('šŸ“¤ Step 8: Pushing skill content...');
71
+ const skillsPushed = await pushSkillContent(destClient, destCustomer, verbose);
72
+ console.log(` āœ… Pushed: ${skillsPushed} skills\n`);
73
+ // Step 9: Create webhooks
74
+ console.log('šŸ“” Step 9: Creating webhooks...');
75
+ result.webhooksCreated = await createWebhooksFromYAML(destClient, destCustomer, verbose);
76
+ console.log(` āœ… Created: ${result.webhooksCreated} webhooks\n`);
77
+ // Step 10: Verify
78
+ console.log('āœ… Step 10: Verifying migration...');
79
+ await verifyMigration(sourceClient, destClient, sourceCustomer, destCustomer);
80
+ result.success = true;
81
+ console.log('\nšŸŽ‰ MIGRATION COMPLETED SUCCESSFULLY!\n');
82
+ }
83
+ catch (error) {
84
+ result.success = false;
85
+ result.errors.push(error instanceof Error ? error.message : String(error));
86
+ console.error(`\nāŒ Migration failed: ${error instanceof Error ? error.message : String(error)}\n`);
87
+ }
88
+ return result;
89
+ }
90
+ async function migrateProjectStructure(sourceClient, destClient, sourceCustomer,
91
+ // @ts-ignore - Parameter kept for API consistency
92
+ destCustomer,
93
+ // @ts-ignore - Parameter kept for future use
94
+ verbose) {
95
+ const sourceProjects = await listProjects(sourceClient);
96
+ const destProjects = await listProjects(destClient);
97
+ const destProjectMap = new Map(destProjects.map(p => [p.idn, p]));
98
+ let projectsCreated = 0;
99
+ let agentsCreated = 0;
100
+ let flowsCreated = 0;
101
+ let skillsCreated = 0;
102
+ for (const sourceProj of sourceProjects) {
103
+ let projectId;
104
+ // Create or get existing project
105
+ const existingProj = destProjectMap.get(sourceProj.idn);
106
+ if (existingProj) {
107
+ projectId = existingProj.id;
108
+ if (verbose)
109
+ console.log(` āœ“ Project ${sourceProj.idn} already exists`);
110
+ }
111
+ else {
112
+ const projResponse = await createProject(destClient, {
113
+ idn: sourceProj.idn,
114
+ title: sourceProj.title,
115
+ description: sourceProj.description || '',
116
+ is_auto_update_enabled: sourceProj.is_auto_update_enabled || false,
117
+ registry_idn: sourceProj.registry_idn || 'production'
118
+ });
119
+ projectId = projResponse.id;
120
+ projectsCreated++;
121
+ if (verbose)
122
+ console.log(` āœ… Created project: ${sourceProj.idn}`);
123
+ }
124
+ // Create agents
125
+ const sourceAgents = await listAgents(sourceClient, sourceProj.id);
126
+ const destAgents = await listAgents(destClient, projectId);
127
+ const destAgentMap = new Map(destAgents.map(a => [a.idn, a]));
128
+ for (const sourceAgent of sourceAgents) {
129
+ let agentId;
130
+ const existingAgent = destAgentMap.get(sourceAgent.idn);
131
+ if (existingAgent) {
132
+ agentId = existingAgent.id;
133
+ }
134
+ else {
135
+ const agentResponse = await createAgent(destClient, projectId, {
136
+ idn: sourceAgent.idn,
137
+ title: sourceAgent.title || sourceAgent.idn,
138
+ description: sourceAgent.description || null
139
+ });
140
+ agentId = agentResponse.id;
141
+ agentsCreated++;
142
+ }
143
+ // Create flows
144
+ const sourceFlows = sourceAgent.flows || [];
145
+ const destAgentData = await listAgents(destClient, projectId);
146
+ const destAgentWithFlows = destAgentData.find(a => a.id === agentId);
147
+ const destFlowMap = new Map((destAgentWithFlows?.flows || []).map(f => [f.idn, f]));
148
+ for (const sourceFlow of sourceFlows) {
149
+ let flowId;
150
+ const existingFlow = destFlowMap.get(sourceFlow.idn);
151
+ if (existingFlow) {
152
+ flowId = existingFlow.id;
153
+ }
154
+ else {
155
+ const flowResponse = await createFlow(destClient, agentId, {
156
+ idn: sourceFlow.idn,
157
+ title: sourceFlow.title
158
+ });
159
+ flowId = flowResponse.id;
160
+ flowsCreated++;
161
+ }
162
+ // Read flow metadata for events and states
163
+ const flowMetaPath = path.join(customerProjectsDir(sourceCustomer.idn), sourceProj.idn, sourceAgent.idn, sourceFlow.idn, 'metadata.yaml');
164
+ if (await fs.pathExists(flowMetaPath)) {
165
+ const flowMeta = yaml.load(await fs.readFile(flowMetaPath, 'utf8'));
166
+ // Create skills
167
+ const destSkills = await listFlowSkills(destClient, flowId);
168
+ const destSkillMap = new Map(destSkills.map(s => [s.idn, s]));
169
+ for (const sourceSkill of flowMeta.skills || []) {
170
+ if (destSkillMap.has(sourceSkill.idn))
171
+ continue;
172
+ try {
173
+ await createSkill(destClient, flowId, {
174
+ idn: sourceSkill.idn,
175
+ title: sourceSkill.title,
176
+ runner_type: sourceSkill.runner_type,
177
+ model: sourceSkill.model,
178
+ prompt_script: ''
179
+ });
180
+ skillsCreated++;
181
+ }
182
+ catch (error) {
183
+ if (verbose && error.response?.status !== 409) {
184
+ console.error(` āš ļø Failed to create skill ${sourceSkill.idn}: ${error.message}`);
185
+ }
186
+ }
187
+ }
188
+ // Create events with full metadata
189
+ for (const event of flowMeta.events || []) {
190
+ try {
191
+ const eventRequest = {
192
+ idn: event.idn,
193
+ description: event.description || event.idn,
194
+ skill_selector: event.skill_selector || 'first',
195
+ interrupt_mode: event.interrupt_mode || 'allow',
196
+ integration_idn: event.integration_idn || '',
197
+ connector_idn: event.connector_idn || ''
198
+ };
199
+ if (event.skill_idn) {
200
+ eventRequest.skill_idn = event.skill_idn;
201
+ }
202
+ if (event.state_idn) {
203
+ eventRequest.state_idn = event.state_idn;
204
+ }
205
+ await createFlowEvent(destClient, flowId, eventRequest);
206
+ }
207
+ catch (error) {
208
+ if (verbose && error.response?.status !== 409 && error.response?.status !== 422) {
209
+ console.error(` āš ļø Failed to create event ${event.idn}: ${error.message}`);
210
+ }
211
+ }
212
+ }
213
+ // Create states
214
+ for (const state of flowMeta.state_fields || []) {
215
+ try {
216
+ await createFlowState(destClient, flowId, {
217
+ title: state.title || state.idn,
218
+ idn: state.idn,
219
+ scope: state.scope || 'flow'
220
+ });
221
+ }
222
+ catch (error) {
223
+ if (verbose && error.response?.status !== 409) {
224
+ console.error(` āš ļø Failed to create state ${state.idn}: ${error.message}`);
225
+ }
226
+ }
227
+ }
228
+ }
229
+ }
230
+ }
231
+ }
232
+ return { projects: projectsCreated, agents: agentsCreated, flows: flowsCreated, skills: skillsCreated };
233
+ }
234
+ async function migrateAttributes(sourceClient, destClient,
235
+ // @ts-ignore - Parameter kept for API consistency
236
+ sourceCustomer,
237
+ // @ts-ignore - Parameter kept for API consistency
238
+ destCustomer,
239
+ // @ts-ignore - Parameter kept for future use
240
+ verbose) {
241
+ let count = 0;
242
+ // Customer attributes
243
+ const sourceAttrs = await getCustomerAttributes(sourceClient, true);
244
+ const destAttrs = await getCustomerAttributes(destClient, true);
245
+ const destAttrMap = new Map(destAttrs.attributes.map(a => [a.idn, a]));
246
+ for (const sourceAttr of sourceAttrs.attributes) {
247
+ const destAttr = destAttrMap.get(sourceAttr.idn);
248
+ if (!destAttr) {
249
+ await createCustomerAttribute(destClient, {
250
+ idn: sourceAttr.idn,
251
+ title: sourceAttr.title,
252
+ description: sourceAttr.description || '',
253
+ value: typeof sourceAttr.value === 'object' ? JSON.stringify(sourceAttr.value) : sourceAttr.value,
254
+ value_type: sourceAttr.value_type,
255
+ group: sourceAttr.group || '',
256
+ is_hidden: sourceAttr.is_hidden || false,
257
+ possible_values: sourceAttr.possible_values || []
258
+ });
259
+ count++;
260
+ }
261
+ else if (JSON.stringify(destAttr.value) !== JSON.stringify(sourceAttr.value)) {
262
+ if (destAttr.id) {
263
+ await updateCustomerAttribute(destClient, {
264
+ ...sourceAttr,
265
+ id: destAttr.id
266
+ });
267
+ count++;
268
+ }
269
+ }
270
+ }
271
+ // Project attributes
272
+ const sourceProjects = await listProjects(sourceClient);
273
+ const destProjects = await listProjects(destClient);
274
+ const destProjectMap = new Map(destProjects.map(p => [p.idn, p]));
275
+ for (const sourceProj of sourceProjects) {
276
+ const destProj = destProjectMap.get(sourceProj.idn);
277
+ if (!destProj)
278
+ continue;
279
+ const sourceProjAttrs = await getProjectAttributes(sourceClient, sourceProj.id, true);
280
+ const destProjAttrs = await getProjectAttributes(destClient, destProj.id, true);
281
+ const destProjAttrMap = new Map(destProjAttrs.attributes.map(a => [a.idn, a]));
282
+ for (const sourceAttr of sourceProjAttrs.attributes) {
283
+ const destAttr = destProjAttrMap.get(sourceAttr.idn);
284
+ if (!destAttr) {
285
+ await createProjectAttribute(destClient, destProj.id, {
286
+ idn: sourceAttr.idn,
287
+ title: sourceAttr.title,
288
+ description: sourceAttr.description || '',
289
+ value: typeof sourceAttr.value === 'object' ? JSON.stringify(sourceAttr.value) : sourceAttr.value,
290
+ value_type: sourceAttr.value_type,
291
+ group: sourceAttr.group || '',
292
+ is_hidden: sourceAttr.is_hidden || false,
293
+ possible_values: sourceAttr.possible_values || []
294
+ });
295
+ count++;
296
+ }
297
+ else if (JSON.stringify(destAttr.value) !== JSON.stringify(sourceAttr.value)) {
298
+ await updateProjectAttribute(destClient, destProj.id, {
299
+ ...destAttr,
300
+ value: sourceAttr.value,
301
+ title: sourceAttr.title,
302
+ description: sourceAttr.description
303
+ });
304
+ count++;
305
+ }
306
+ }
307
+ }
308
+ return count;
309
+ }
310
+ async function migrateAKB(sourceClient, destClient,
311
+ // @ts-ignore - Parameter kept for future use
312
+ verbose) {
313
+ const sourcePersonas = await searchPersonas(sourceClient, true);
314
+ const destPersonas = await searchPersonas(destClient, true);
315
+ const destPersonaMap = new Map(destPersonas.items.map(p => [p.name, p]));
316
+ let personasCreated = 0;
317
+ let articlesImported = 0;
318
+ for (const sourcePersona of sourcePersonas.items) {
319
+ if (destPersonaMap.has(sourcePersona.name))
320
+ continue;
321
+ const newPersona = await createPersona(destClient, {
322
+ name: sourcePersona.name,
323
+ title: sourcePersona.title,
324
+ description: sourcePersona.description || ''
325
+ });
326
+ personasCreated++;
327
+ // Import articles
328
+ let page = 1;
329
+ let hasMore = true;
330
+ while (hasMore) {
331
+ const topics = await getAkbTopics(sourceClient, sourcePersona.id, page, 50);
332
+ if (topics.items.length === 0)
333
+ break;
334
+ for (const topicItem of topics.items) {
335
+ const articleData = {
336
+ persona_id: newPersona.id,
337
+ topic_name: topicItem.topic.topic_name,
338
+ source: topicItem.topic.source || '',
339
+ topic_summary: topicItem.topic.topic_summary || '',
340
+ topic_facts: topicItem.topic.topic_facts || [],
341
+ labels: topicItem.topic.labels || [],
342
+ confidence: topicItem.topic.confidence || 1.0
343
+ };
344
+ await importAkbArticle(destClient, articleData);
345
+ articlesImported++;
346
+ }
347
+ page++;
348
+ hasMore = topics.items.length >= 50;
349
+ }
350
+ }
351
+ return { personas: personasCreated, articles: articlesImported };
352
+ }
353
+ async function migrateIntegrationConnectors(sourceClient, destClient,
354
+ // @ts-ignore - Parameter kept for future use
355
+ verbose) {
356
+ const sourceIntegrations = await listIntegrations(sourceClient);
357
+ const destIntegrations = await listIntegrations(destClient);
358
+ const destIntMap = new Map(destIntegrations.map(i => [i.idn, i]));
359
+ let connectorsCreated = 0;
360
+ for (const sourceInt of sourceIntegrations) {
361
+ const destInt = destIntMap.get(sourceInt.idn);
362
+ if (!destInt)
363
+ continue;
364
+ const sourceConnectors = await listConnectors(sourceClient, sourceInt.id);
365
+ const destConnectors = await listConnectors(destClient, destInt.id);
366
+ const destConnMap = new Map(destConnectors.map(c => [c.connector_idn, c]));
367
+ for (const sourceConn of sourceConnectors) {
368
+ if (destConnMap.has(sourceConn.connector_idn))
369
+ continue;
370
+ try {
371
+ await createConnector(destClient, destInt.id, {
372
+ title: sourceConn.title,
373
+ connector_idn: sourceConn.connector_idn,
374
+ integration_idn: sourceInt.idn,
375
+ settings: sourceConn.settings
376
+ });
377
+ connectorsCreated++;
378
+ }
379
+ catch (error) {
380
+ if (verbose && error.response?.status !== 409) {
381
+ console.error(` āš ļø Failed to create connector ${sourceConn.connector_idn}: ${error.message}`);
382
+ }
383
+ }
384
+ }
385
+ }
386
+ return connectorsCreated;
387
+ }
388
+ async function copyAccountFiles(sourceIdn, destIdn) {
389
+ const sourceDir = customerDir(sourceIdn);
390
+ const destDir = customerDir(destIdn);
391
+ await fs.ensureDir(destDir);
392
+ // Copy projects
393
+ const sourceProjects = path.join(sourceDir, 'projects');
394
+ const destProjects = path.join(destDir, 'projects');
395
+ if (await fs.pathExists(sourceProjects)) {
396
+ await fs.copy(sourceProjects, destProjects);
397
+ }
398
+ // Copy integrations
399
+ const sourceIntegrations = path.join(sourceDir, 'integrations');
400
+ const destIntegrations = path.join(destDir, 'integrations');
401
+ if (await fs.pathExists(sourceIntegrations)) {
402
+ await fs.copy(sourceIntegrations, destIntegrations);
403
+ }
404
+ // Copy AKB
405
+ const sourceAkb = path.join(sourceDir, 'akb');
406
+ const destAkb = path.join(destDir, 'akb');
407
+ if (await fs.pathExists(sourceAkb)) {
408
+ await fs.copy(sourceAkb, destAkb);
409
+ }
410
+ // Copy attributes
411
+ const sourceAttrs = path.join(sourceDir, 'attributes.yaml');
412
+ const destAttrs = path.join(destDir, 'attributes.yaml');
413
+ if (await fs.pathExists(sourceAttrs)) {
414
+ await fs.copy(sourceAttrs, destAttrs);
415
+ }
416
+ }
417
+ async function buildMapFromAPI(destClient, destCustomer,
418
+ // @ts-ignore - Parameter kept for future use
419
+ verbose) {
420
+ const newoDir = path.join('.newo', destCustomer.idn);
421
+ await fs.ensureDir(newoDir);
422
+ const projects = await listProjects(destClient);
423
+ const projectMap = { projects: {} };
424
+ for (const project of projects.filter(p => p.idn !== 'test')) {
425
+ const agents = await listAgents(destClient, project.id);
426
+ const projectData = {
427
+ projectId: project.id,
428
+ projectIdn: project.idn,
429
+ agents: {}
430
+ };
431
+ for (const agent of agents) {
432
+ projectData.agents[agent.idn] = {
433
+ id: agent.id,
434
+ flows: {}
435
+ };
436
+ for (const flow of agent.flows || []) {
437
+ const skills = await listFlowSkills(destClient, flow.id);
438
+ const skillMap = {};
439
+ for (const skill of skills) {
440
+ skillMap[skill.idn] = {
441
+ id: skill.id,
442
+ idn: skill.idn,
443
+ title: skill.title,
444
+ runner_type: skill.runner_type,
445
+ model: skill.model,
446
+ parameters: skill.parameters,
447
+ path: skill.path
448
+ };
449
+ }
450
+ projectData.agents[agent.idn].flows[flow.idn] = {
451
+ id: flow.id,
452
+ skills: skillMap
453
+ };
454
+ }
455
+ }
456
+ projectMap.projects[project.idn] = projectData;
457
+ }
458
+ await fs.writeJson(path.join(newoDir, 'map.json'), projectMap, { spaces: 2 });
459
+ await fs.writeJson(path.join(newoDir, 'hashes.json'), {}, { spaces: 2 });
460
+ }
461
+ async function pushSkillContent(destClient, destCustomer,
462
+ // @ts-ignore - Parameter kept for future use
463
+ verbose) {
464
+ const mapPath = path.join('.newo', destCustomer.idn, 'map.json');
465
+ const projectMap = await fs.readJson(mapPath);
466
+ const destDir = customerDir(destCustomer.idn);
467
+ let pushedCount = 0;
468
+ for (const [projectIdn, projectData] of Object.entries(projectMap.projects || {})) {
469
+ const typedProjectData = projectData;
470
+ for (const [agentIdn, agentData] of Object.entries(typedProjectData.agents || {})) {
471
+ const typedAgentData = agentData;
472
+ for (const [flowIdn, flowData] of Object.entries(typedAgentData.flows || {})) {
473
+ const typedFlowData = flowData;
474
+ for (const [skillIdn, skillData] of Object.entries(typedFlowData.skills || {})) {
475
+ const typedSkillData = skillData;
476
+ const extension = typedSkillData.runner_type === 'nsl' ? 'jinja' : 'guidance';
477
+ const skillFilePath = path.join(destDir, 'projects', projectIdn, agentIdn, flowIdn, skillIdn, `${skillIdn}.${extension}`);
478
+ if (await fs.pathExists(skillFilePath)) {
479
+ const content = await fs.readFile(skillFilePath, 'utf8');
480
+ if (content.trim().length > 0) {
481
+ try {
482
+ await updateSkill(destClient, {
483
+ ...typedSkillData,
484
+ prompt_script: content
485
+ });
486
+ pushedCount++;
487
+ if (pushedCount % 100 === 0 && verbose) {
488
+ console.log(` Progress: ${pushedCount} skills pushed...`);
489
+ }
490
+ }
491
+ catch (error) {
492
+ if (verbose) {
493
+ console.error(` āš ļø Failed to push ${skillIdn}: ${error.message}`);
494
+ }
495
+ }
496
+ }
497
+ }
498
+ }
499
+ }
500
+ }
501
+ }
502
+ return pushedCount;
503
+ }
504
+ async function createWebhooksFromYAML(destClient, destCustomer,
505
+ // @ts-ignore - Parameter kept for future use
506
+ verbose) {
507
+ const destDir = customerDir(destCustomer.idn);
508
+ let webhooksCreated = 0;
509
+ // Outgoing webhooks
510
+ const outgoingFile = path.join(destDir, 'integrations/api/connectors/webhook/webhooks/outgoing.yaml');
511
+ if (await fs.pathExists(outgoingFile)) {
512
+ const outgoingData = yaml.load(await fs.readFile(outgoingFile, 'utf8'));
513
+ const webhooks = outgoingData.webhooks || [];
514
+ for (const webhook of webhooks) {
515
+ try {
516
+ await destClient.post('/api/v1/webhooks', {
517
+ idn: webhook.idn,
518
+ description: webhook.description || '',
519
+ connector_idn: webhook.connector_idn,
520
+ url: webhook.url,
521
+ command_idns: webhook.command_idns || []
522
+ });
523
+ webhooksCreated++;
524
+ }
525
+ catch (error) {
526
+ if (error.response?.status !== 409 && verbose) {
527
+ console.error(` āš ļø Failed to create webhook ${webhook.idn}: ${error.message}`);
528
+ }
529
+ }
530
+ }
531
+ }
532
+ // Incoming webhooks
533
+ const incomingFile = path.join(destDir, 'integrations/api/connectors/webhook/webhooks/incoming.yaml');
534
+ if (await fs.pathExists(incomingFile)) {
535
+ const incomingData = yaml.load(await fs.readFile(incomingFile, 'utf8'));
536
+ const webhooks = incomingData.webhooks || [];
537
+ for (const webhook of webhooks) {
538
+ try {
539
+ await destClient.post('/api/v1/webhooks/incoming', {
540
+ idn: webhook.idn,
541
+ description: webhook.description || '',
542
+ connector_idn: webhook.connector_idn,
543
+ event_idns: webhook.event_idns || [],
544
+ allowed_ips: webhook.allowed_ips || []
545
+ });
546
+ webhooksCreated++;
547
+ }
548
+ catch (error) {
549
+ if (error.response?.status !== 409 && verbose) {
550
+ console.error(` āš ļø Failed to create webhook ${webhook.idn}: ${error.message}`);
551
+ }
552
+ }
553
+ }
554
+ }
555
+ return webhooksCreated;
556
+ }
557
+ async function verifyMigration(sourceClient, destClient,
558
+ // @ts-ignore - Parameter kept for API consistency
559
+ sourceCustomer,
560
+ // @ts-ignore - Parameter kept for API consistency
561
+ destCustomer) {
562
+ const sourceProjects = await listProjects(sourceClient);
563
+ const destProjects = await listProjects(destClient);
564
+ let srcSkills = 0;
565
+ let dstSkills = 0;
566
+ for (const proj of sourceProjects) {
567
+ const agents = await listAgents(sourceClient, proj.id);
568
+ for (const agent of agents) {
569
+ for (const flow of agent.flows || []) {
570
+ const skills = await listFlowSkills(sourceClient, flow.id);
571
+ srcSkills += skills.length;
572
+ }
573
+ }
574
+ }
575
+ for (const proj of destProjects.filter(p => p.idn !== 'test')) {
576
+ const agents = await listAgents(destClient, proj.id);
577
+ for (const agent of agents) {
578
+ for (const flow of agent.flows || []) {
579
+ const skills = await listFlowSkills(destClient, flow.id);
580
+ dstSkills += skills.length;
581
+ }
582
+ }
583
+ }
584
+ console.log(` Skills: ${srcSkills} source → ${dstSkills} destination ${srcSkills === dstSkills ? 'āœ…' : 'āŒ'}`);
585
+ if (srcSkills !== dstSkills) {
586
+ throw new Error(`Skill count mismatch: ${srcSkills} source vs ${dstSkills} destination`);
587
+ }
588
+ console.log(' āœ… Verification passed');
589
+ }
590
+ //# sourceMappingURL=migrate.js.map
package/dist/sync.d.ts CHANGED
@@ -7,5 +7,6 @@ export { status } from './sync/status.js';
7
7
  export { pullSingleProject, pullAll } from './sync/projects.js';
8
8
  export { pushChanged } from './sync/push.js';
9
9
  export { generateFlowsYaml } from './sync/metadata.js';
10
+ export { migrateAccount } from './sync/migrate.js';
10
11
  export { isProjectMap, isLegacyProjectMap } from './sync/projects.js';
11
12
  //# sourceMappingURL=sync.d.ts.map
package/dist/sync.js CHANGED
@@ -8,6 +8,7 @@ export { status } from './sync/status.js';
8
8
  export { pullSingleProject, pullAll } from './sync/projects.js';
9
9
  export { pushChanged } from './sync/push.js';
10
10
  export { generateFlowsYaml } from './sync/metadata.js';
11
+ export { migrateAccount } from './sync/migrate.js';
11
12
  // Re-export type guards for backward compatibility
12
13
  export { isProjectMap, isLegacyProjectMap } from './sync/projects.js';
13
14
  //# sourceMappingURL=sync.js.map
package/dist/types.d.ts CHANGED
@@ -66,6 +66,13 @@ export interface ProjectMeta {
66
66
  readonly idn: string;
67
67
  readonly title: string;
68
68
  readonly description?: string;
69
+ readonly version?: string | null;
70
+ readonly is_synchronized?: boolean;
71
+ readonly preferred_update_time?: string | null;
72
+ readonly is_auto_update_enabled?: boolean;
73
+ readonly registry_idn?: string;
74
+ readonly registry_item_idn?: string | null;
75
+ readonly registry_item_version?: string | null;
69
76
  readonly created_at?: string;
70
77
  readonly updated_at?: string;
71
78
  }
@@ -73,8 +80,15 @@ export interface Agent {
73
80
  readonly id: string;
74
81
  readonly idn: string;
75
82
  readonly title?: string;
76
- readonly description?: string;
83
+ readonly description?: string | null;
77
84
  readonly flows?: readonly Flow[];
85
+ readonly persona?: {
86
+ readonly id: string;
87
+ readonly name: string;
88
+ readonly title: string;
89
+ } | null;
90
+ readonly created_at?: string;
91
+ readonly updated_at?: string;
78
92
  }
79
93
  export interface Flow {
80
94
  readonly id: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "newo",
3
- "version": "3.2.0",
4
- "description": "NEWO CLI: Professional command-line tool with modular architecture for NEWO AI Agent development. Features integration management, webhook sync, AKB knowledge base, project attributes, sandbox testing, IDN-based file management, real-time progress tracking, intelligent sync operations, and comprehensive multi-customer support.",
3
+ "version": "3.3.0",
4
+ "description": "NEWO CLI: Professional command-line tool with modular architecture for NEWO AI Agent development. Features account migration, integration management, webhook automation, AKB knowledge base, project attributes, sandbox testing, IDN-based file management, real-time progress tracking, intelligent sync operations, and comprehensive multi-customer support.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "newo": "dist/cli.js"
@@ -40,7 +40,11 @@
40
40
  "customer-attributes",
41
41
  "nsl",
42
42
  "guidance",
43
- "skill-management"
43
+ "skill-management",
44
+ "account-migration",
45
+ "migration",
46
+ "verification",
47
+ "webhook-automation"
44
48
  ],
45
49
  "author": "sabbah13",
46
50
  "license": "MIT",