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