newo 3.3.3 → 3.4.1

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.
Files changed (83) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/api.d.ts +6 -1
  3. package/dist/api.js +63 -1
  4. package/dist/application/migration/MigrationEngine.d.ts +141 -0
  5. package/dist/application/migration/MigrationEngine.js +322 -0
  6. package/dist/application/migration/index.d.ts +5 -0
  7. package/dist/application/migration/index.js +5 -0
  8. package/dist/application/sync/SyncEngine.d.ts +134 -0
  9. package/dist/application/sync/SyncEngine.js +335 -0
  10. package/dist/application/sync/index.d.ts +5 -0
  11. package/dist/application/sync/index.js +5 -0
  12. package/dist/cli/commands/add-project.d.ts +3 -0
  13. package/dist/cli/commands/add-project.js +136 -0
  14. package/dist/cli/commands/create-customer.d.ts +3 -0
  15. package/dist/cli/commands/create-customer.js +159 -0
  16. package/dist/cli/commands/diff.d.ts +6 -0
  17. package/dist/cli/commands/diff.js +288 -0
  18. package/dist/cli/commands/help.js +75 -4
  19. package/dist/cli/commands/list-registries.d.ts +3 -0
  20. package/dist/cli/commands/list-registries.js +39 -0
  21. package/dist/cli/commands/list-registry-items.d.ts +3 -0
  22. package/dist/cli/commands/list-registry-items.js +112 -0
  23. package/dist/cli/commands/logs.d.ts +18 -0
  24. package/dist/cli/commands/logs.js +283 -0
  25. package/dist/cli/commands/pull.js +114 -10
  26. package/dist/cli/commands/push.js +122 -12
  27. package/dist/cli/commands/watch.d.ts +6 -0
  28. package/dist/cli/commands/watch.js +195 -0
  29. package/dist/cli-new/bootstrap.d.ts +74 -0
  30. package/dist/cli-new/bootstrap.js +154 -0
  31. package/dist/cli-new/di/Container.d.ts +64 -0
  32. package/dist/cli-new/di/Container.js +122 -0
  33. package/dist/cli-new/di/tokens.d.ts +77 -0
  34. package/dist/cli-new/di/tokens.js +76 -0
  35. package/dist/cli.js +28 -0
  36. package/dist/domain/resources/common/types.d.ts +71 -0
  37. package/dist/domain/resources/common/types.js +42 -0
  38. package/dist/domain/strategies/sync/AkbSyncStrategy.d.ts +63 -0
  39. package/dist/domain/strategies/sync/AkbSyncStrategy.js +274 -0
  40. package/dist/domain/strategies/sync/AttributeSyncStrategy.d.ts +87 -0
  41. package/dist/domain/strategies/sync/AttributeSyncStrategy.js +378 -0
  42. package/dist/domain/strategies/sync/ConversationSyncStrategy.d.ts +61 -0
  43. package/dist/domain/strategies/sync/ConversationSyncStrategy.js +232 -0
  44. package/dist/domain/strategies/sync/ISyncStrategy.d.ts +149 -0
  45. package/dist/domain/strategies/sync/ISyncStrategy.js +24 -0
  46. package/dist/domain/strategies/sync/IntegrationSyncStrategy.d.ts +68 -0
  47. package/dist/domain/strategies/sync/IntegrationSyncStrategy.js +413 -0
  48. package/dist/domain/strategies/sync/ProjectSyncStrategy.d.ts +111 -0
  49. package/dist/domain/strategies/sync/ProjectSyncStrategy.js +523 -0
  50. package/dist/domain/strategies/sync/index.d.ts +13 -0
  51. package/dist/domain/strategies/sync/index.js +19 -0
  52. package/dist/sync/migrate.js +99 -23
  53. package/dist/types.d.ts +162 -0
  54. package/package.json +3 -1
  55. package/src/api.ts +77 -2
  56. package/src/application/migration/MigrationEngine.ts +492 -0
  57. package/src/application/migration/index.ts +5 -0
  58. package/src/application/sync/SyncEngine.ts +467 -0
  59. package/src/application/sync/index.ts +5 -0
  60. package/src/cli/commands/add-project.ts +159 -0
  61. package/src/cli/commands/create-customer.ts +185 -0
  62. package/src/cli/commands/diff.ts +360 -0
  63. package/src/cli/commands/help.ts +75 -4
  64. package/src/cli/commands/list-registries.ts +53 -0
  65. package/src/cli/commands/list-registry-items.ts +149 -0
  66. package/src/cli/commands/logs.ts +329 -0
  67. package/src/cli/commands/pull.ts +128 -11
  68. package/src/cli/commands/push.ts +131 -13
  69. package/src/cli/commands/watch.ts +227 -0
  70. package/src/cli-new/bootstrap.ts +252 -0
  71. package/src/cli-new/di/Container.ts +152 -0
  72. package/src/cli-new/di/tokens.ts +105 -0
  73. package/src/cli.ts +35 -0
  74. package/src/domain/resources/common/types.ts +106 -0
  75. package/src/domain/strategies/sync/AkbSyncStrategy.ts +358 -0
  76. package/src/domain/strategies/sync/AttributeSyncStrategy.ts +508 -0
  77. package/src/domain/strategies/sync/ConversationSyncStrategy.ts +299 -0
  78. package/src/domain/strategies/sync/ISyncStrategy.ts +182 -0
  79. package/src/domain/strategies/sync/IntegrationSyncStrategy.ts +522 -0
  80. package/src/domain/strategies/sync/ProjectSyncStrategy.ts +747 -0
  81. package/src/domain/strategies/sync/index.ts +46 -0
  82. package/src/sync/migrate.ts +103 -24
  83. package/src/types.ts +178 -0
@@ -156,38 +156,94 @@ verbose) {
156
156
  idn: sourceFlow.idn,
157
157
  title: sourceFlow.title
158
158
  });
159
- flowId = flowResponse.id;
159
+ // Handle "pending-sync" response - re-fetch to get actual flow ID
160
+ if (flowResponse.id === 'pending-sync' || !flowResponse.id.match(/^[0-9a-f-]{36}$/i)) {
161
+ // Retry with increasing wait times
162
+ let createdFlow = null;
163
+ for (const waitMs of [1500, 3000, 5000]) {
164
+ await new Promise(resolve => setTimeout(resolve, waitMs));
165
+ const refreshedAgents = await listAgents(destClient, projectId);
166
+ const refreshedAgent = refreshedAgents.find(a => a.id === agentId);
167
+ createdFlow = refreshedAgent?.flows?.find(f => f.idn === sourceFlow.idn);
168
+ if (createdFlow)
169
+ break;
170
+ }
171
+ if (createdFlow) {
172
+ flowId = createdFlow.id;
173
+ }
174
+ else {
175
+ if (verbose)
176
+ console.error(` ⚠️ Flow ${sourceFlow.idn} created but could not retrieve ID after retries`);
177
+ continue; // Skip this flow
178
+ }
179
+ }
180
+ else {
181
+ flowId = flowResponse.id;
182
+ }
160
183
  flowsCreated++;
161
184
  }
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);
185
+ // Flow directory path
186
+ const flowDirPath = path.join(customerProjectsDir(sourceCustomer.idn), sourceProj.idn, sourceAgent.idn, sourceFlow.idn);
187
+ if (await fs.pathExists(flowDirPath)) {
188
+ // Read flow metadata for events and states
189
+ const flowMetaPath = path.join(flowDirPath, 'metadata.yaml');
190
+ const flowMeta = await fs.pathExists(flowMetaPath)
191
+ ? yaml.load(await fs.readFile(flowMetaPath, 'utf8'))
192
+ : {};
193
+ // Create skills by reading skill subdirectories
194
+ let destSkills;
195
+ try {
196
+ destSkills = await listFlowSkills(destClient, flowId);
197
+ }
198
+ catch (err) {
199
+ if (verbose)
200
+ console.error(` ⚠️ Failed to list skills for flow ${sourceFlow.idn} (${flowId}): ${err.message}`);
201
+ continue; // Skip this flow if we can't list skills
202
+ }
168
203
  const destSkillMap = new Map(destSkills.map(s => [s.idn, s]));
169
- for (const sourceSkill of flowMeta.skills || []) {
170
- if (destSkillMap.has(sourceSkill.idn))
204
+ // Get all subdirectories in flow directory (these are skills)
205
+ const flowDirContents = await fs.readdir(flowDirPath, { withFileTypes: true });
206
+ const skillDirs = flowDirContents.filter(d => d.isDirectory());
207
+ for (const skillDir of skillDirs) {
208
+ const skillIdn = skillDir.name;
209
+ if (destSkillMap.has(skillIdn))
171
210
  continue;
211
+ // Read skill metadata
212
+ const skillMetaPath = path.join(flowDirPath, skillIdn, 'metadata.yaml');
213
+ if (!await fs.pathExists(skillMetaPath))
214
+ continue;
215
+ const skillMeta = yaml.load(await fs.readFile(skillMetaPath, 'utf8'));
172
216
  try {
173
217
  await createSkill(destClient, flowId, {
174
- idn: sourceSkill.idn,
175
- title: sourceSkill.title,
176
- runner_type: sourceSkill.runner_type,
177
- model: sourceSkill.model,
218
+ idn: skillMeta.idn || skillIdn,
219
+ title: skillMeta.title || skillIdn,
220
+ runner_type: skillMeta.runner_type || 'guidance',
221
+ model: skillMeta.model,
178
222
  prompt_script: ''
179
223
  });
180
224
  skillsCreated++;
225
+ if (verbose)
226
+ console.log(` ✅ Created skill: ${skillIdn}`);
181
227
  }
182
228
  catch (error) {
183
229
  if (verbose && error.response?.status !== 409) {
184
- console.error(` ⚠️ Failed to create skill ${sourceSkill.idn}: ${error.message}`);
230
+ console.error(` ⚠️ Failed to create skill ${skillIdn}: ${error.message}`);
185
231
  }
186
232
  }
187
233
  }
188
- // Create events with full metadata
234
+ // Get current skills in destination to validate event references
235
+ const currentDestSkills = await listFlowSkills(destClient, flowId);
236
+ const destSkillIdnSet = new Set(currentDestSkills.map(s => s.idn));
237
+ // Create events with full metadata (only if referenced skill exists)
189
238
  for (const event of flowMeta.events || []) {
190
239
  try {
240
+ // Skip events that reference skills not yet created
241
+ if (event.skill_idn && !destSkillIdnSet.has(event.skill_idn)) {
242
+ if (verbose) {
243
+ console.log(` ⏭️ Skipping event ${event.idn} - skill ${event.skill_idn} not yet created`);
244
+ }
245
+ continue;
246
+ }
191
247
  const eventRequest = {
192
248
  idn: event.idn,
193
249
  description: event.description || event.idn,
@@ -205,8 +261,10 @@ verbose) {
205
261
  await createFlowEvent(destClient, flowId, eventRequest);
206
262
  }
207
263
  catch (error) {
208
- if (verbose && error.response?.status !== 409 && error.response?.status !== 422) {
209
- console.error(` ⚠️ Failed to create event ${event.idn}: ${error.message}`);
264
+ // Don't log 409 (already exists) or 422 (validation) errors as they're expected
265
+ if (error.response?.status !== 409 && error.response?.status !== 422) {
266
+ if (verbose)
267
+ console.error(` ⚠️ Failed to create event ${event.idn}: ${error.message}`);
210
268
  }
211
269
  }
212
270
  }
@@ -350,9 +408,7 @@ verbose) {
350
408
  }
351
409
  return { personas: personasCreated, articles: articlesImported };
352
410
  }
353
- async function migrateIntegrationConnectors(sourceClient, destClient,
354
- // @ts-ignore - Parameter kept for future use
355
- verbose) {
411
+ async function migrateIntegrationConnectors(sourceClient, destClient, verbose) {
356
412
  const sourceIntegrations = await listIntegrations(sourceClient);
357
413
  const destIntegrations = await listIntegrations(destClient);
358
414
  const destIntMap = new Map(destIntegrations.map(i => [i.idn, i]));
@@ -361,24 +417,44 @@ verbose) {
361
417
  const destInt = destIntMap.get(sourceInt.idn);
362
418
  if (!destInt)
363
419
  continue;
420
+ // Build set of allowed setting idns from destination integration schema
421
+ const destSettingIdns = new Set((destInt.connector_settings_descriptions || []).map(s => s.idn));
364
422
  const sourceConnectors = await listConnectors(sourceClient, sourceInt.id);
365
423
  const destConnectors = await listConnectors(destClient, destInt.id);
366
424
  const destConnMap = new Map(destConnectors.map(c => [c.connector_idn, c]));
367
425
  for (const sourceConn of sourceConnectors) {
368
426
  if (destConnMap.has(sourceConn.connector_idn))
369
427
  continue;
428
+ // Filter settings to only include those that exist in destination schema
429
+ const filteredSettings = sourceConn.settings.filter(s => {
430
+ const allowed = destSettingIdns.size === 0 || destSettingIdns.has(s.idn);
431
+ if (!allowed && verbose) {
432
+ console.log(` ⏭️ Skipping setting ${s.idn} for ${sourceConn.connector_idn} (not in dest schema)`);
433
+ }
434
+ return allowed;
435
+ });
436
+ // Log if settings were filtered
437
+ if (filteredSettings.length !== sourceConn.settings.length && verbose) {
438
+ const skippedCount = sourceConn.settings.length - filteredSettings.length;
439
+ console.log(` 📋 Filtered ${skippedCount} settings for ${sourceConn.connector_idn}`);
440
+ }
370
441
  try {
371
442
  await createConnector(destClient, destInt.id, {
372
443
  title: sourceConn.title,
373
444
  connector_idn: sourceConn.connector_idn,
374
445
  integration_idn: sourceInt.idn,
375
- settings: sourceConn.settings
446
+ settings: filteredSettings
376
447
  });
377
448
  connectorsCreated++;
449
+ if (verbose) {
450
+ console.log(` ✅ Created connector: ${sourceConn.connector_idn} (${sourceInt.idn})`);
451
+ }
378
452
  }
379
453
  catch (error) {
380
- if (verbose && error.response?.status !== 409) {
381
- console.error(` ⚠️ Failed to create connector ${sourceConn.connector_idn}: ${error.message}`);
454
+ // Always log connector creation failures (not just in verbose mode)
455
+ const errMsg = error.response?.data?.reason || error.message;
456
+ if (error.response?.status !== 409) {
457
+ console.error(` ⚠️ Failed to create connector ${sourceConn.connector_idn} (${sourceInt.idn}): ${errMsg}`);
382
458
  }
383
459
  }
384
460
  }
package/dist/types.d.ts CHANGED
@@ -494,6 +494,20 @@ export interface PublishFlowRequest {
494
494
  export interface PublishFlowResponse {
495
495
  success: boolean;
496
496
  }
497
+ export interface IntegrationSettingDescription {
498
+ readonly title: string;
499
+ readonly idn: string;
500
+ readonly control_type: string;
501
+ readonly value_type: string;
502
+ readonly is_required: boolean;
503
+ readonly is_readonly: boolean;
504
+ readonly default_value?: string | null;
505
+ readonly description?: string | null;
506
+ readonly group?: string | null;
507
+ readonly possible_values?: readonly string[] | null;
508
+ readonly possible_values_only?: boolean;
509
+ readonly possible_values_url?: string | null;
510
+ }
497
511
  export interface Integration {
498
512
  readonly id: string;
499
513
  readonly title: string;
@@ -501,6 +515,8 @@ export interface Integration {
501
515
  readonly description: string;
502
516
  readonly is_disabled: boolean;
503
517
  readonly channel: string;
518
+ readonly integration_settings_descriptions?: readonly IntegrationSettingDescription[];
519
+ readonly connector_settings_descriptions?: readonly IntegrationSettingDescription[];
504
520
  }
505
521
  export interface IntegrationSetting {
506
522
  readonly title: string;
@@ -703,4 +719,150 @@ export interface AkbYamlTopic {
703
719
  labels: string[];
704
720
  topic_summary: string;
705
721
  }
722
+ export interface Registry {
723
+ readonly id: string;
724
+ readonly idn: string;
725
+ readonly account_id: string;
726
+ readonly is_public: boolean;
727
+ }
728
+ export interface RegistryItemProjectImage {
729
+ readonly id: string;
730
+ readonly idn: string;
731
+ readonly image_hash: string;
732
+ readonly storage_id: string;
733
+ readonly customer_idn: string;
734
+ readonly account_id: string;
735
+ readonly created_at: string;
736
+ }
737
+ export interface RegistryItem {
738
+ readonly id: string;
739
+ readonly idn: string;
740
+ readonly version: string;
741
+ readonly project_image: RegistryItemProjectImage;
742
+ readonly account_id: string;
743
+ readonly account_idn: string | null;
744
+ readonly account_email: string | null;
745
+ readonly is_public: boolean;
746
+ readonly active_project_count: number;
747
+ readonly created_at: string;
748
+ readonly published_at: string;
749
+ }
750
+ export interface AddProjectFromRegistryRequest {
751
+ idn: string;
752
+ title: string;
753
+ version?: string;
754
+ description?: string;
755
+ is_auto_update_enabled?: boolean;
756
+ registry_idn: string;
757
+ registry_item_idn: string;
758
+ registry_item_version: string | null;
759
+ }
760
+ export interface CustomerMember {
761
+ email: string;
762
+ role: 'owner' | 'account_manager' | 'technical_owner' | 'referral';
763
+ tenants?: string[];
764
+ first_name?: string;
765
+ external_account_id?: string;
766
+ contact_email?: string;
767
+ }
768
+ export interface CustomerAttributeInput {
769
+ idn: string;
770
+ value: string;
771
+ is_hidden?: boolean;
772
+ group?: string;
773
+ }
774
+ export interface CustomerProjectInput {
775
+ idn: string;
776
+ title?: string;
777
+ registry_idn?: string;
778
+ registry_item_idn?: string;
779
+ registry_item_version?: string;
780
+ is_auto_update_enabled?: boolean;
781
+ }
782
+ export interface CreateNewoCustomerRequest {
783
+ secret: string;
784
+ customer: {
785
+ organization_name: string;
786
+ tenant: string;
787
+ comment?: string;
788
+ query_params?: string;
789
+ members: CustomerMember[];
790
+ contact_email: string;
791
+ contact_phone?: string;
792
+ external_customer_id?: string;
793
+ organization_type?: 'customer' | 'partner';
794
+ organization_status?: 'temporal' | 'permanent';
795
+ billing_method?: string;
796
+ platform_links?: {
797
+ portal?: string;
798
+ builder?: string;
799
+ creator?: string;
800
+ chat_widget?: string;
801
+ };
802
+ organization_logo?: {
803
+ title?: string;
804
+ external_logo_id?: string;
805
+ };
806
+ attributes?: CustomerAttributeInput[];
807
+ a_service?: {
808
+ partner_idn: string;
809
+ member_idn: string;
810
+ };
811
+ };
812
+ projects?: CustomerProjectInput[];
813
+ }
814
+ export interface CreateNewoCustomerResponse {
815
+ id: string;
816
+ idn: string;
817
+ }
818
+ export type LogLevel = 'info' | 'warning' | 'error';
819
+ export type LogType = 'system' | 'operation' | 'call';
820
+ export interface LogEntry {
821
+ readonly log_id: string;
822
+ readonly level: LogLevel;
823
+ readonly log_type: LogType;
824
+ readonly project_idn: string;
825
+ readonly data: {
826
+ readonly external_event_id?: string;
827
+ readonly reference_idn?: string;
828
+ readonly operation_type?: string;
829
+ readonly arguments?: readonly {
830
+ name: string;
831
+ value: string;
832
+ }[];
833
+ readonly user_actor_id?: string;
834
+ readonly integration_idn?: string;
835
+ readonly connector_idn?: string;
836
+ readonly to_integration_idn?: string;
837
+ readonly to_connector_idn?: string;
838
+ readonly flow_idn?: string;
839
+ readonly skill_idn?: string;
840
+ readonly agent_idn?: string;
841
+ readonly line?: number;
842
+ readonly runtime_context_id?: string;
843
+ readonly [key: string]: unknown;
844
+ };
845
+ readonly message: string;
846
+ readonly datetime: string;
847
+ }
848
+ export interface LogsResponse {
849
+ readonly items: readonly LogEntry[];
850
+ }
851
+ export interface LogsQueryParams {
852
+ page?: number;
853
+ per?: number;
854
+ from_datetime?: string;
855
+ to_datetime?: string;
856
+ levels?: LogLevel | LogLevel[];
857
+ log_types?: LogType | LogType[];
858
+ message?: string;
859
+ project_idn?: string;
860
+ flow_idn?: string;
861
+ skill_idn?: string;
862
+ external_event_id?: string;
863
+ runtime_context_id?: string;
864
+ user_persona_ids?: string;
865
+ user_actor_ids?: string;
866
+ agent_persona_ids?: string;
867
+ }
706
868
  //# sourceMappingURL=types.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "newo",
3
- "version": "3.3.3",
3
+ "version": "3.4.1",
4
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": {
@@ -61,6 +61,7 @@
61
61
  },
62
62
  "dependencies": {
63
63
  "axios": "^1.7.7",
64
+ "chokidar": "^5.0.0",
64
65
  "dotenv": "^16.4.5",
65
66
  "fs-extra": "^11.2.0",
66
67
  "js-yaml": "^4.1.0",
@@ -69,6 +70,7 @@
69
70
  },
70
71
  "devDependencies": {
71
72
  "@types/chai": "^4.3.11",
73
+ "@types/chokidar": "^1.7.5",
72
74
  "@types/fs-extra": "^11.0.4",
73
75
  "@types/js-yaml": "^4.0.9",
74
76
  "@types/minimist": "^1.2.5",
package/src/api.ts CHANGED
@@ -52,7 +52,14 @@ import type {
52
52
  OutgoingWebhook,
53
53
  IncomingWebhook,
54
54
  PersonaSearchResponse,
55
- AkbTopicsResponse
55
+ AkbTopicsResponse,
56
+ Registry,
57
+ RegistryItem,
58
+ AddProjectFromRegistryRequest,
59
+ CreateNewoCustomerRequest,
60
+ CreateNewoCustomerResponse,
61
+ LogsQueryParams,
62
+ LogsResponse
56
63
  } from './types.js';
57
64
 
58
65
  // Per-request retry tracking to avoid shared state issues
@@ -103,7 +110,7 @@ export async function makeClient(verbose: boolean = false, token?: string): Prom
103
110
  // Use per-request retry tracking to avoid shared state issues
104
111
  const config = error.config as InternalAxiosRequestConfig & { [RETRY_SYMBOL]?: boolean };
105
112
 
106
- if (status === 401 && !config?.[RETRY_SYMBOL]) {
113
+ if ((status === 401 || status === 403) && !config?.[RETRY_SYMBOL]) {
107
114
  if (config) {
108
115
  config[RETRY_SYMBOL] = true;
109
116
  if (verbose) console.log('🔄 Retrying with fresh token...');
@@ -552,4 +559,72 @@ export async function createIncomingWebhook(
552
559
  ): Promise<{ id: string; url: string }> {
553
560
  const response = await client.post('/api/v1/webhooks/incoming', webhookData);
554
561
  return response.data;
562
+ }
563
+
564
+ // Registry API Functions
565
+
566
+ export async function listRegistries(client: AxiosInstance): Promise<Registry[]> {
567
+ const response = await client.get<Registry[]>('/api/v1/designer/registries');
568
+ return response.data;
569
+ }
570
+
571
+ export async function listRegistryItems(client: AxiosInstance, registryId: string): Promise<RegistryItem[]> {
572
+ const response = await client.get<RegistryItem[]>(`/api/v1/designer/registries/${registryId}/items`);
573
+ return response.data;
574
+ }
575
+
576
+ export async function addProjectFromRegistry(
577
+ client: AxiosInstance,
578
+ projectData: AddProjectFromRegistryRequest
579
+ ): Promise<CreateProjectResponse> {
580
+ // Uses the same endpoint as createProject, but with registry fields populated
581
+ const response = await client.post<CreateProjectResponse>('/api/v1/designer/projects', projectData);
582
+ return response.data;
583
+ }
584
+
585
+ // Create NEWO Customer (v3 API - creates a new customer account)
586
+ export async function createNewoCustomer(
587
+ client: AxiosInstance,
588
+ customerData: CreateNewoCustomerRequest
589
+ ): Promise<CreateNewoCustomerResponse> {
590
+ const response = await client.post<CreateNewoCustomerResponse>('/api/v3/customer', customerData);
591
+ return response.data;
592
+ }
593
+
594
+ // Analytics Logs API
595
+ export async function getLogs(
596
+ client: AxiosInstance,
597
+ params: LogsQueryParams
598
+ ): Promise<LogsResponse> {
599
+ // Build query params, handling arrays for levels and log_types
600
+ const queryParams = new URLSearchParams();
601
+
602
+ if (params.page !== undefined) queryParams.set('page', String(params.page));
603
+ if (params.per !== undefined) queryParams.set('per', String(params.per));
604
+ if (params.from_datetime) queryParams.set('from_datetime', params.from_datetime);
605
+ if (params.to_datetime) queryParams.set('to_datetime', params.to_datetime);
606
+ if (params.message) queryParams.set('message', params.message);
607
+ if (params.project_idn) queryParams.set('project_idn', params.project_idn);
608
+ if (params.flow_idn) queryParams.set('flow_idn', params.flow_idn);
609
+ if (params.skill_idn) queryParams.set('skill_idn', params.skill_idn);
610
+ if (params.external_event_id) queryParams.set('external_event_id', params.external_event_id);
611
+ if (params.runtime_context_id) queryParams.set('runtime_context_id', params.runtime_context_id);
612
+ if (params.user_persona_ids) queryParams.set('user_persona_ids', params.user_persona_ids);
613
+ if (params.user_actor_ids) queryParams.set('user_actor_ids', params.user_actor_ids);
614
+ if (params.agent_persona_ids) queryParams.set('agent_persona_ids', params.agent_persona_ids);
615
+
616
+ // Handle levels (can be single or array)
617
+ if (params.levels) {
618
+ const levelsValue = Array.isArray(params.levels) ? params.levels.join(',') : params.levels;
619
+ queryParams.set('levels', levelsValue);
620
+ }
621
+
622
+ // Handle log_types (can be single or array)
623
+ if (params.log_types) {
624
+ const typesValue = Array.isArray(params.log_types) ? params.log_types.join(',') : params.log_types;
625
+ queryParams.set('log_types', typesValue);
626
+ }
627
+
628
+ const response = await client.get<LogsResponse>(`/api/v1/analytics/logs?${queryParams.toString()}`);
629
+ return response.data;
555
630
  }