newo 1.9.2 → 2.0.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 (66) hide show
  1. package/CHANGELOG.md +116 -0
  2. package/README.md +68 -20
  3. package/dist/cli/commands/conversations.d.ts +3 -0
  4. package/dist/cli/commands/conversations.js +38 -0
  5. package/dist/cli/commands/help.d.ts +5 -0
  6. package/dist/cli/commands/help.js +50 -0
  7. package/dist/cli/commands/import-akb.d.ts +3 -0
  8. package/dist/cli/commands/import-akb.js +62 -0
  9. package/dist/cli/commands/list-customers.d.ts +3 -0
  10. package/dist/cli/commands/list-customers.js +13 -0
  11. package/dist/cli/commands/meta.d.ts +3 -0
  12. package/dist/cli/commands/meta.js +19 -0
  13. package/dist/cli/commands/pull-attributes.d.ts +3 -0
  14. package/dist/cli/commands/pull-attributes.js +16 -0
  15. package/dist/cli/commands/pull.d.ts +3 -0
  16. package/dist/cli/commands/pull.js +34 -0
  17. package/dist/cli/commands/push.d.ts +3 -0
  18. package/dist/cli/commands/push.js +39 -0
  19. package/dist/cli/commands/status.d.ts +3 -0
  20. package/dist/cli/commands/status.js +22 -0
  21. package/dist/cli/customer-selection.d.ts +23 -0
  22. package/dist/cli/customer-selection.js +110 -0
  23. package/dist/cli/errors.d.ts +9 -0
  24. package/dist/cli/errors.js +111 -0
  25. package/dist/cli.js +66 -463
  26. package/dist/fsutil.js +1 -1
  27. package/dist/sync/attributes.d.ts +7 -0
  28. package/dist/sync/attributes.js +90 -0
  29. package/dist/sync/conversations.d.ts +7 -0
  30. package/dist/sync/conversations.js +218 -0
  31. package/dist/sync/metadata.d.ts +8 -0
  32. package/dist/sync/metadata.js +124 -0
  33. package/dist/sync/projects.d.ts +13 -0
  34. package/dist/sync/projects.js +298 -0
  35. package/dist/sync/push.d.ts +7 -0
  36. package/dist/sync/push.js +171 -0
  37. package/dist/sync/skill-files.d.ts +43 -0
  38. package/dist/sync/skill-files.js +123 -0
  39. package/dist/sync/status.d.ts +6 -0
  40. package/dist/sync/status.js +247 -0
  41. package/dist/sync.d.ts +10 -8
  42. package/dist/sync.js +12 -1226
  43. package/dist/types.d.ts +0 -1
  44. package/package.json +2 -2
  45. package/src/cli/commands/conversations.ts +47 -0
  46. package/src/cli/commands/help.ts +50 -0
  47. package/src/cli/commands/import-akb.ts +71 -0
  48. package/src/cli/commands/list-customers.ts +14 -0
  49. package/src/cli/commands/meta.ts +26 -0
  50. package/src/cli/commands/pull-attributes.ts +23 -0
  51. package/src/cli/commands/pull.ts +43 -0
  52. package/src/cli/commands/push.ts +47 -0
  53. package/src/cli/commands/status.ts +30 -0
  54. package/src/cli/customer-selection.ts +135 -0
  55. package/src/cli/errors.ts +111 -0
  56. package/src/cli.ts +77 -471
  57. package/src/fsutil.ts +1 -1
  58. package/src/sync/attributes.ts +110 -0
  59. package/src/sync/conversations.ts +257 -0
  60. package/src/sync/metadata.ts +153 -0
  61. package/src/sync/projects.ts +372 -0
  62. package/src/sync/push.ts +200 -0
  63. package/src/sync/skill-files.ts +181 -0
  64. package/src/sync/status.ts +277 -0
  65. package/src/sync.ts +14 -1418
  66. package/src/types.ts +0 -1
package/dist/cli.js CHANGED
@@ -1,115 +1,21 @@
1
1
  #!/usr/bin/env node
2
+ /**
3
+ * NEWO CLI - Main entry point using modular architecture
4
+ */
2
5
  import minimist from 'minimist';
3
6
  import dotenv from 'dotenv';
4
- import { makeClient, getProjectMeta, importAkbArticle } from './api.js';
5
- import { pullAll, pushChanged, status, saveCustomerAttributes, pullConversations } from './sync.js';
6
- import { parseAkbFile, prepareArticlesForImport } from './akb.js';
7
- import { initializeEnvironment, ENV, EnvValidationError } from './env.js';
8
- import { parseCustomerConfigAsync, listCustomers, getCustomer, getDefaultCustomer, tryGetDefaultCustomer, getAllCustomers, validateCustomerConfig } from './customerAsync.js';
9
- import { getValidAccessToken } from './auth.js';
10
- import path from 'path';
11
- // Enhanced error logging for CLI
12
- function logCliError(level, message, meta) {
13
- const timestamp = new Date().toISOString();
14
- const logEntry = {
15
- timestamp,
16
- level,
17
- module: 'cli',
18
- message,
19
- ...meta
20
- };
21
- // Only log JSON format in verbose mode, otherwise use clean user messages
22
- const verbose = process.argv.includes('--verbose') || process.argv.includes('-v');
23
- if (verbose) {
24
- if (level === 'error') {
25
- console.error(JSON.stringify(logEntry));
26
- }
27
- else if (level === 'warn') {
28
- console.warn(JSON.stringify(logEntry));
29
- }
30
- else {
31
- console.log(JSON.stringify(logEntry));
32
- }
33
- }
34
- else {
35
- // Clean user-facing messages
36
- if (level === 'error') {
37
- console.error(`āŒ ${message}`);
38
- }
39
- else if (level === 'warn') {
40
- console.warn(`āš ļø ${message}`);
41
- }
42
- else {
43
- console.log(`ā„¹ļø ${message}`);
44
- }
45
- }
46
- }
47
- // Enhanced error handling with user-friendly messages
48
- function handleCliError(error, operation = 'operation') {
49
- const verbose = process.argv.includes('--verbose') || process.argv.includes('-v');
50
- if (error instanceof Error) {
51
- // Authentication errors
52
- if (error.message.includes('API key') || error.message.includes('Authentication failed')) {
53
- logCliError('error', 'Authentication failed. Please check your API key configuration.');
54
- if (!verbose) {
55
- console.error('\nšŸ’” Troubleshooting tips:');
56
- console.error(' • Verify your API key is correct in .env file');
57
- console.error(' • For multi-customer setup, check NEWO_CUSTOMER_<IDN>_API_KEY');
58
- console.error(' • Run with --verbose for detailed error information');
59
- }
60
- }
61
- // Network errors
62
- else if (error.message.includes('Network timeout') || error.message.includes('ENOTFOUND') || error.message.includes('ECONNREFUSED')) {
63
- logCliError('error', 'Network connection failed. Please check your internet connection.');
64
- if (!verbose) {
65
- console.error('\nšŸ’” Troubleshooting tips:');
66
- console.error(' • Check your internet connection');
67
- console.error(' • Verify NEWO_BASE_URL is correct');
68
- console.error(' • Try again in a few moments');
69
- }
70
- }
71
- // Environment configuration errors
72
- else if (error instanceof EnvValidationError || error.message.includes('not set')) {
73
- logCliError('error', 'Configuration error. Please check your environment setup.');
74
- if (!verbose) {
75
- console.error('\nšŸ’” Setup help:');
76
- console.error(' • Copy .env.example to .env and configure your settings');
77
- console.error(' • Run "newo --help" to see configuration examples');
78
- console.error(' • Check the README for detailed setup instructions');
79
- }
80
- }
81
- // File system errors
82
- else if (error.message.includes('ENOENT') || error.message.includes('EACCES')) {
83
- logCliError('error', 'File system error. Please check file permissions and paths.');
84
- }
85
- // Rate limiting
86
- else if (error.message.includes('Rate limit exceeded')) {
87
- logCliError('error', 'Rate limit exceeded. Please wait before trying again.');
88
- }
89
- // General API errors
90
- else if (error.message.includes('response') || error.message.includes('status')) {
91
- logCliError('error', `API error during ${operation}. Please try again or contact support.`);
92
- }
93
- // Unknown errors
94
- else {
95
- logCliError('error', `Unexpected error during ${operation}: ${error.message}`);
96
- if (!verbose) {
97
- console.error('\nšŸ’” For more details, run the command with --verbose flag');
98
- }
99
- }
100
- if (verbose) {
101
- logCliError('error', 'Full error details', {
102
- operation,
103
- errorType: error.constructor.name,
104
- stack: error.stack?.split('\n').slice(0, 5).join('\n') // First 5 lines of stack
105
- });
106
- }
107
- }
108
- else {
109
- logCliError('error', `Unknown error during ${operation}: ${String(error)}`);
110
- }
111
- process.exit(1);
112
- }
7
+ import { initializeEnvironment, ENV } from './env.js';
8
+ import { parseAndValidateCustomerConfig } from './cli/customer-selection.js';
9
+ import { handleCliError, logCliError } from './cli/errors.js';
10
+ import { handlePullCommand } from './cli/commands/pull.js';
11
+ import { handlePushCommand } from './cli/commands/push.js';
12
+ import { handleStatusCommand } from './cli/commands/status.js';
13
+ import { handleConversationsCommand } from './cli/commands/conversations.js';
14
+ import { handleMetaCommand } from './cli/commands/meta.js';
15
+ import { handlePullAttributesCommand } from './cli/commands/pull-attributes.js';
16
+ import { handleImportAkbCommand } from './cli/commands/import-akb.js';
17
+ import { handleHelpCommand } from './cli/commands/help.js';
18
+ import { handleListCustomersCommand } from './cli/commands/list-customers.js';
113
19
  dotenv.config();
114
20
  async function main() {
115
21
  try {
@@ -117,378 +23,69 @@ async function main() {
117
23
  initializeEnvironment();
118
24
  }
119
25
  catch (error) {
120
- if (error instanceof EnvValidationError) {
121
- console.error('Environment validation failed:', error.message);
122
- process.exit(1);
123
- }
124
- throw error;
26
+ console.error('Environment validation failed:', error instanceof Error ? error.message : String(error));
27
+ process.exit(1);
125
28
  }
126
29
  const args = minimist(process.argv.slice(2));
127
30
  const cmd = args._[0];
128
31
  const verbose = Boolean(args.verbose || args.v);
129
- // Parse customer configuration (async for API key array support)
130
- let customerConfig;
131
- try {
132
- customerConfig = await parseCustomerConfigAsync(ENV, verbose);
133
- validateCustomerConfig(customerConfig);
134
- }
135
- catch (error) {
136
- logCliError('error', 'Failed to parse customer configuration');
137
- if (error instanceof Error) {
138
- logCliError('error', error.message);
139
- }
140
- process.exit(1);
141
- }
142
- // Handle customer selection
143
- let selectedCustomer = null;
144
- let allCustomers = [];
145
- if (cmd === 'list-customers') {
146
- const customers = listCustomers(customerConfig);
147
- console.log('Available customers:');
148
- for (const customerIdn of customers) {
149
- const isDefault = customerConfig.defaultCustomer === customerIdn;
150
- console.log(` ${customerIdn}${isDefault ? ' (default)' : ''}`);
151
- }
152
- return;
153
- }
154
- // Customer selection logic moved inside command processing to avoid early failures
155
32
  if (verbose)
156
33
  console.log(`šŸ” Command parsed: "${cmd}"`);
34
+ // Handle help command first
157
35
  if (!cmd || ['help', '-h', '--help'].includes(cmd)) {
158
- console.log(`NEWO CLI - Multi-Customer Support
159
- Usage:
160
- newo pull [--customer <idn>] # download projects -> ./newo_customers/<idn>/projects/
161
- newo push [--customer <idn>] # upload modified *.guidance/*.jinja back to NEWO
162
- newo status [--customer <idn>] # show modified files
163
- newo conversations [--customer <idn>] [--all] # download user conversations -> ./newo_customers/<idn>/conversations.yaml
164
- newo list-customers # list available customers
165
- newo meta [--customer <idn>] # get project metadata (debug)
166
- newo import-akb <file> <persona_id> [--customer <idn>] # import AKB articles from file
167
-
168
- Flags:
169
- --customer <idn> # specify customer (if not set, uses default or interactive selection)
170
- --all # include all available data (for conversations: all personas and acts)
171
- --verbose, -v # enable detailed logging
172
-
173
- Environment Variables:
174
- NEWO_BASE_URL # NEWO API base URL (default: https://app.newo.ai)
175
- NEWO_CUSTOMER_<IDN>_API_KEY # API key for customer <IDN>
176
- NEWO_CUSTOMER_<IDN>_PROJECT_ID # Optional: specific project ID for customer
177
- NEWO_DEFAULT_CUSTOMER # Optional: default customer to use
178
-
179
- Multi-Customer Examples:
180
- # Configure customers in .env:
181
- NEWO_CUSTOMER_acme_API_KEY=your_acme_api_key
182
- NEWO_CUSTOMER_globex_API_KEY=your_globex_api_key
183
- NEWO_DEFAULT_CUSTOMER=acme
184
-
185
- # Commands:
186
- newo pull # Pull from all customers (if no default set)
187
- newo pull --customer acme # Pull projects for Acme only
188
- newo status # Status for all customers (if no default set)
189
- newo push # Interactive selection for multiple customers
190
- newo push --customer globex # Push changes for Globex only
191
-
192
- File Structure:
193
- newo_customers/
194
- ā”œā”€ā”€ acme/
195
- │ └── projects/
196
- │ └── project1/
197
- └── globex/
198
- └── projects/
199
- └── project2/
200
- `);
201
- return;
202
- }
203
- if (verbose)
204
- console.log(`šŸ” Starting command processing for: ${cmd}`);
205
- if (cmd === 'pull') {
206
- // Handle customer selection for pull command
207
- if (args.customer) {
208
- const customer = getCustomer(customerConfig, args.customer);
209
- if (!customer) {
210
- console.error(`Unknown customer: ${args.customer}`);
211
- console.error(`Available customers: ${listCustomers(customerConfig).join(', ')}`);
212
- process.exit(1);
213
- }
214
- selectedCustomer = customer;
215
- }
216
- else {
217
- // Try to get default, fall back to all customers
218
- selectedCustomer = tryGetDefaultCustomer(customerConfig);
219
- if (!selectedCustomer) {
220
- allCustomers = getAllCustomers(customerConfig);
221
- if (verbose)
222
- console.log(`šŸ“„ No default customer specified, pulling from all ${allCustomers.length} customers`);
223
- }
224
- }
225
- if (selectedCustomer) {
226
- // Single customer pull
227
- const accessToken = await getValidAccessToken(selectedCustomer);
228
- const client = await makeClient(verbose, accessToken);
229
- const projectId = selectedCustomer.projectId || null;
230
- await pullAll(client, selectedCustomer, projectId, verbose);
231
- }
232
- else if (allCustomers.length > 0) {
233
- // Multi-customer pull
234
- console.log(`šŸ”„ Pulling from ${allCustomers.length} customers...`);
235
- for (const customer of allCustomers) {
236
- console.log(`\nšŸ“„ Pulling from customer: ${customer.idn}`);
237
- const accessToken = await getValidAccessToken(customer);
238
- const client = await makeClient(verbose, accessToken);
239
- const projectId = customer.projectId || null;
240
- await pullAll(client, customer, projectId, verbose);
241
- }
242
- console.log(`\nāœ… Pull completed for all ${allCustomers.length} customers`);
243
- }
244
- return;
245
- }
246
- if (cmd === 'status') {
247
- // Handle customer selection for status command
248
- if (args.customer) {
249
- const customer = getCustomer(customerConfig, args.customer);
250
- if (!customer) {
251
- console.error(`Unknown customer: ${args.customer}`);
252
- console.error(`Available customers: ${listCustomers(customerConfig).join(', ')}`);
253
- process.exit(1);
254
- }
255
- selectedCustomer = customer;
256
- }
257
- else {
258
- // Try to get default, fall back to all customers
259
- selectedCustomer = tryGetDefaultCustomer(customerConfig);
260
- if (!selectedCustomer) {
261
- allCustomers = getAllCustomers(customerConfig);
262
- console.log(`šŸ”„ Checking status for ${allCustomers.length} customers...`);
263
- }
264
- }
265
- if (selectedCustomer) {
266
- // Single customer status
267
- await status(selectedCustomer, verbose);
268
- }
269
- else if (allCustomers.length > 0) {
270
- // Multi-customer status
271
- for (const customer of allCustomers) {
272
- console.log(`\nšŸ“‹ Status for customer: ${customer.idn}`);
273
- await status(customer, verbose);
274
- }
275
- console.log(`\nāœ… Status check completed for all ${allCustomers.length} customers`);
276
- }
277
- return;
278
- }
279
- if (cmd === 'push') {
280
- // Handle customer selection for push command
281
- if (args.customer) {
282
- const customer = getCustomer(customerConfig, args.customer);
283
- if (!customer) {
284
- console.error(`Unknown customer: ${args.customer}`);
285
- console.error(`Available customers: ${listCustomers(customerConfig).join(', ')}`);
286
- process.exit(1);
287
- }
288
- selectedCustomer = customer;
289
- }
290
- else {
291
- // Try to get default, provide interactive selection if multiple exist
292
- selectedCustomer = tryGetDefaultCustomer(customerConfig);
293
- if (!selectedCustomer) {
294
- // Multiple customers exist with no default, ask user
295
- allCustomers = getAllCustomers(customerConfig);
296
- console.log(`\nšŸ“¤ Multiple customers available for push:`);
297
- allCustomers.forEach((customer, index) => {
298
- console.log(` ${index + 1}. ${customer.idn}`);
299
- });
300
- console.log(` ${allCustomers.length + 1}. All customers`);
301
- const readline = await import('readline');
302
- const rl = readline.createInterface({
303
- input: process.stdin,
304
- output: process.stdout
305
- });
306
- const choice = await new Promise((resolve) => {
307
- rl.question(`\nSelect customer to push (1-${allCustomers.length + 1}): `, resolve);
308
- });
309
- rl.close();
310
- const choiceNum = parseInt(choice.trim());
311
- if (choiceNum === allCustomers.length + 1) {
312
- // User selected "All customers"
313
- console.log(`šŸ”„ Pushing to all ${allCustomers.length} customers...`);
314
- }
315
- else if (choiceNum >= 1 && choiceNum <= allCustomers.length) {
316
- // User selected specific customer
317
- selectedCustomer = allCustomers[choiceNum - 1] || null;
318
- allCustomers = []; // Clear to indicate single customer mode
319
- if (selectedCustomer) {
320
- console.log(`šŸ”„ Pushing to customer: ${selectedCustomer.idn}`);
321
- }
322
- }
323
- else {
324
- console.error('Invalid choice. Exiting.');
325
- process.exit(1);
326
- }
327
- }
328
- }
329
- if (selectedCustomer) {
330
- // Single customer push
331
- const accessToken = await getValidAccessToken(selectedCustomer);
332
- const client = await makeClient(verbose, accessToken);
333
- await pushChanged(client, selectedCustomer, verbose);
334
- }
335
- else if (allCustomers.length > 0) {
336
- // Multi-customer push (user selected "All customers")
337
- console.log(`šŸ”„ Pushing to ${allCustomers.length} customers...`);
338
- for (const customer of allCustomers) {
339
- console.log(`\nšŸ“¤ Pushing for customer: ${customer.idn}`);
340
- const accessToken = await getValidAccessToken(customer);
341
- const client = await makeClient(verbose, accessToken);
342
- await pushChanged(client, customer, verbose);
343
- }
344
- console.log(`\nāœ… Push completed for all ${allCustomers.length} customers`);
345
- }
36
+ handleHelpCommand();
346
37
  return;
347
38
  }
348
- if (cmd === 'conversations') {
349
- // Handle customer selection for conversations command
350
- if (args.customer) {
351
- const customer = getCustomer(customerConfig, args.customer);
352
- if (!customer) {
353
- console.error(`Unknown customer: ${args.customer}`);
354
- console.error(`Available customers: ${listCustomers(customerConfig).join(', ')}`);
355
- process.exit(1);
356
- }
357
- selectedCustomer = customer;
358
- }
359
- else {
360
- // Try to get default, fall back to all customers
361
- selectedCustomer = tryGetDefaultCustomer(customerConfig);
362
- if (!selectedCustomer) {
363
- allCustomers = getAllCustomers(customerConfig);
364
- if (verbose)
365
- console.log(`šŸ’¬ No default customer specified, pulling conversations from all ${allCustomers.length} customers`);
366
- }
367
- }
368
- // Parse conversation-specific options - load all data by default
369
- const conversationOptions = {
370
- includeAll: true, // Always include all data for conversations
371
- maxPersonas: undefined, // No limit on personas
372
- maxActsPerPersona: undefined // No limit on acts per persona
373
- };
374
- if (selectedCustomer) {
375
- // Single customer conversations
376
- const accessToken = await getValidAccessToken(selectedCustomer);
377
- const client = await makeClient(verbose, accessToken);
378
- console.log(`šŸ’¬ Pulling conversations for customer: ${selectedCustomer.idn} (all data)`);
379
- await pullConversations(client, selectedCustomer, conversationOptions, verbose);
380
- console.log(`āœ… Conversations saved to newo_customers/${selectedCustomer.idn}/conversations.yaml`);
381
- }
382
- else if (allCustomers.length > 0) {
383
- // Multi-customer conversations
384
- console.log(`šŸ’¬ Pulling conversations from ${allCustomers.length} customers (all data)...`);
385
- for (const customer of allCustomers) {
386
- console.log(`\nšŸ’¬ Pulling conversations for customer: ${customer.idn}`);
387
- const accessToken = await getValidAccessToken(customer);
388
- const client = await makeClient(verbose, accessToken);
389
- await pullConversations(client, customer, conversationOptions, verbose);
390
- }
391
- console.log(`\nāœ… Conversations pull completed for all ${allCustomers.length} customers`);
392
- }
393
- return;
394
- }
395
- // For all other commands, require a single selected customer
396
- if (args.customer) {
397
- const customer = getCustomer(customerConfig, args.customer);
398
- if (!customer) {
399
- console.error(`Unknown customer: ${args.customer}`);
400
- console.error(`Available customers: ${listCustomers(customerConfig).join(', ')}`);
401
- process.exit(1);
402
- }
403
- selectedCustomer = customer;
404
- }
405
- else {
39
+ // Handle list-customers command (doesn't need full customer config)
40
+ if (cmd === 'list-customers') {
406
41
  try {
407
- selectedCustomer = getDefaultCustomer(customerConfig);
42
+ const customerConfig = await parseAndValidateCustomerConfig(ENV, verbose);
43
+ handleListCustomersCommand(customerConfig);
44
+ return;
408
45
  }
409
46
  catch (error) {
410
- const message = error instanceof Error ? error.message : String(error);
411
- console.error(message);
412
- process.exit(1);
47
+ handleCliError(error, 'list-customers');
413
48
  }
414
49
  }
415
- if (!selectedCustomer) {
416
- console.error('Customer selection required for this command');
417
- process.exit(1);
418
- }
419
- // Get access token for the selected customer
420
- const accessToken = await getValidAccessToken(selectedCustomer);
421
- const client = await makeClient(verbose, accessToken);
422
- if (cmd === 'meta') {
423
- if (!selectedCustomer.projectId) {
424
- console.error(`No project ID configured for customer ${selectedCustomer.idn}`);
425
- console.error(`Set NEWO_CUSTOMER_${selectedCustomer.idn.toUpperCase()}_PROJECT_ID in your .env file`);
426
- process.exit(1);
427
- }
428
- const meta = await getProjectMeta(client, selectedCustomer.projectId);
429
- console.log(JSON.stringify(meta, null, 2));
430
- }
431
- else if (cmd === 'pull-attributes') {
432
- console.log(`šŸ” Fetching customer attributes for ${selectedCustomer.idn}...`);
433
- await saveCustomerAttributes(client, selectedCustomer, verbose);
434
- console.log(`āœ… Customer attributes saved to newo_customers/${selectedCustomer.idn}/attributes.yaml`);
435
- }
436
- else if (cmd === 'import-akb') {
437
- const akbFile = args._[1];
438
- const personaId = args._[2];
439
- if (!akbFile || !personaId) {
440
- console.error('Usage: newo import-akb <file> <persona_id>');
441
- console.error('Example: newo import-akb akb.txt da4550db-2b95-4500-91ff-fb4b60fe7be9');
442
- process.exit(1);
443
- }
444
- const filePath = path.resolve(akbFile);
445
- try {
446
- if (verbose)
447
- console.log(`šŸ“– Parsing AKB file: ${filePath}`);
448
- const articles = await parseAkbFile(filePath);
449
- console.log(`āœ“ Parsed ${articles.length} articles from ${akbFile}`);
450
- if (verbose)
451
- console.log(`šŸ”§ Preparing articles for persona: ${personaId}`);
452
- const preparedArticles = prepareArticlesForImport(articles, personaId);
453
- let successCount = 0;
454
- let errorCount = 0;
455
- console.log(`šŸ“¤ Importing ${preparedArticles.length} articles...`);
456
- for (const [index, article] of preparedArticles.entries()) {
457
- try {
458
- if (verbose) {
459
- console.log(` [${index + 1}/${preparedArticles.length}] Importing ${article.topic_name}...`);
460
- }
461
- await importAkbArticle(client, article);
462
- successCount++;
463
- if (!verbose)
464
- process.stdout.write('.');
465
- }
466
- catch (error) {
467
- errorCount++;
468
- const errorMessage = error instanceof Error && 'response' in error
469
- ? error?.response?.data
470
- : error instanceof Error
471
- ? error.message
472
- : String(error);
473
- console.error(`\nāŒ Failed to import ${article.topic_name}:`, errorMessage);
474
- }
475
- }
476
- if (!verbose)
477
- console.log(''); // new line after dots
478
- console.log(`āœ… Import complete: ${successCount} successful, ${errorCount} failed`);
479
- }
480
- catch (error) {
481
- const message = error instanceof Error ? error.message : String(error);
482
- console.error('āŒ AKB import failed:', message);
483
- process.exit(1);
50
+ // For all other commands, parse and validate customer configuration
51
+ const customerConfig = await parseAndValidateCustomerConfig(ENV, verbose);
52
+ if (verbose)
53
+ console.log(`šŸ” Starting command processing for: ${cmd}`);
54
+ try {
55
+ switch (cmd) {
56
+ case 'pull':
57
+ await handlePullCommand(customerConfig, args, verbose);
58
+ break;
59
+ case 'push':
60
+ await handlePushCommand(customerConfig, args, verbose);
61
+ break;
62
+ case 'status':
63
+ await handleStatusCommand(customerConfig, args, verbose);
64
+ break;
65
+ case 'conversations':
66
+ await handleConversationsCommand(customerConfig, args, verbose);
67
+ break;
68
+ case 'meta':
69
+ await handleMetaCommand(customerConfig, args, verbose);
70
+ break;
71
+ case 'pull-attributes':
72
+ await handlePullAttributesCommand(customerConfig, args, verbose);
73
+ break;
74
+ case 'import-akb':
75
+ await handleImportAkbCommand(customerConfig, args, verbose);
76
+ break;
77
+ default:
78
+ console.error('Unknown command:', cmd);
79
+ console.error('Run "newo --help" for usage information');
80
+ process.exit(1);
484
81
  }
485
82
  }
486
- else {
487
- console.error('Unknown command:', cmd);
488
- process.exit(1);
83
+ catch (error) {
84
+ handleCliError(error, cmd);
489
85
  }
490
86
  }
491
- main().catch((error) => {
87
+ // Global error handler
88
+ process.on('unhandledRejection', (error) => {
492
89
  // Determine operation context from command line args
493
90
  const args = process.argv.slice(2);
494
91
  const cmd = args.find(arg => !arg.startsWith('-')) || 'unknown command';
@@ -503,4 +100,10 @@ main().catch((error) => {
503
100
  }
504
101
  handleCliError(error, cmd);
505
102
  });
103
+ // Start the CLI
104
+ main().catch((error) => {
105
+ const args = process.argv.slice(2);
106
+ const cmd = args.find(arg => !arg.startsWith('-')) || 'unknown command';
107
+ handleCliError(error, cmd);
108
+ });
506
109
  //# sourceMappingURL=cli.js.map
package/dist/fsutil.js CHANGED
@@ -48,7 +48,7 @@ export function skillFolderPath(customerIdn, projectIdn, agentIdn, flowIdn, skil
48
48
  }
49
49
  export function skillScriptPath(customerIdn, projectIdn, agentIdn, flowIdn, skillIdn, runnerType = 'guidance') {
50
50
  const extension = runnerType === 'nsl' ? '.jinja' : '.guidance';
51
- return path.posix.join(skillFolderPath(customerIdn, projectIdn, agentIdn, flowIdn, skillIdn), `skill${extension}`);
51
+ return path.posix.join(skillFolderPath(customerIdn, projectIdn, agentIdn, flowIdn, skillIdn), `${skillIdn}${extension}`);
52
52
  }
53
53
  // Metadata paths for hierarchical structure
54
54
  export function projectMetadataPath(customerIdn, projectIdn) {
@@ -0,0 +1,7 @@
1
+ import type { AxiosInstance } from 'axios';
2
+ import type { CustomerConfig } from '../types.js';
3
+ /**
4
+ * Save customer attributes to YAML format and return content for hashing
5
+ */
6
+ export declare function saveCustomerAttributes(client: AxiosInstance, customer: CustomerConfig, verbose?: boolean): Promise<string>;
7
+ //# sourceMappingURL=attributes.d.ts.map