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/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,137 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.9.3] - 2025-09-17
|
|
9
|
+
|
|
10
|
+
### šļø Major Architecture Refactoring
|
|
11
|
+
|
|
12
|
+
**CLI Module Restructuring:**
|
|
13
|
+
- **BREAKING**: Main CLI entry point moved from `dist/cli.js` to `dist/cli/index.js`
|
|
14
|
+
- Split monolithic 500+ line `cli.ts` into focused modules:
|
|
15
|
+
- `cli/index.ts` - Main entry point and command routing
|
|
16
|
+
- `cli/errors.ts` - Centralized error handling with user-friendly messages
|
|
17
|
+
- `cli/customer-selection.ts` - Customer management and selection logic
|
|
18
|
+
- `cli/commands/` - Individual command handlers (pull, push, status, conversations, etc.)
|
|
19
|
+
|
|
20
|
+
**Sync Module Restructuring:**
|
|
21
|
+
- Extracted key functions from 1400+ line `sync.ts` into focused modules:
|
|
22
|
+
- `sync/attributes.ts` - Customer attributes synchronization
|
|
23
|
+
- `sync/conversations.ts` - Conversation history management
|
|
24
|
+
- `sync/status.ts` - File status checking and change detection
|
|
25
|
+
- `sync/index.ts` - Unified exports with backward compatibility
|
|
26
|
+
|
|
27
|
+
**Testing Infrastructure:**
|
|
28
|
+
- Fixed all test imports to use compiled JavaScript from `dist/` directory
|
|
29
|
+
- Converted test utilities from CommonJS to ES modules
|
|
30
|
+
- Improved test isolation and ES module compatibility
|
|
31
|
+
|
|
32
|
+
### š§ Technical Improvements
|
|
33
|
+
- **Maintainability**: Single-responsibility modules with clear interfaces
|
|
34
|
+
- **Testability**: Each module can be tested independently with clear dependencies
|
|
35
|
+
- **Readability**: Focused modules replace hard-to-navigate monolithic files
|
|
36
|
+
- **Extensibility**: Easy to add new commands and sync operations
|
|
37
|
+
- **Type Safety**: Enhanced TypeScript compilation with zero errors
|
|
38
|
+
|
|
39
|
+
### š Documentation Updates
|
|
40
|
+
- Updated CLAUDE.md with new modular architecture documentation
|
|
41
|
+
- Added comprehensive module descriptions and benefits
|
|
42
|
+
- Updated development patterns for the new structure
|
|
43
|
+
|
|
44
|
+
## [2.0.0] - 2025-09-17
|
|
45
|
+
|
|
46
|
+
### š Major Release: Professional Modular Architecture
|
|
47
|
+
|
|
48
|
+
**BREAKING CHANGES**
|
|
49
|
+
- **IDN-Based File Naming**: Skills now saved as `{skillIdn}.jinja/.guidance` instead of `skill.jinja/.guidance`
|
|
50
|
+
- **Automatic Migration**: Existing files automatically renamed to IDN format during pull
|
|
51
|
+
- **Enhanced File Validation**: Multiple script files per skill now generate warnings and block push operations
|
|
52
|
+
|
|
53
|
+
### šļø Complete Architecture Refactoring
|
|
54
|
+
|
|
55
|
+
**CLI Module Restructuring:**
|
|
56
|
+
- **Entry Point**: `src/cli.ts` (124 lines) - Clean main entry using modular imports
|
|
57
|
+
- **Error Handling**: `src/cli/errors.ts` - Centralized error management with user-friendly messages
|
|
58
|
+
- **Customer Management**: `src/cli/customer-selection.ts` - Reusable customer selection logic
|
|
59
|
+
- **Command Handlers**: `src/cli/commands/` - Individual handlers for all commands
|
|
60
|
+
|
|
61
|
+
**Sync Module Restructuring:**
|
|
62
|
+
- **Entry Point**: `src/sync.ts` (13 lines) - Unified exports with re-exports
|
|
63
|
+
- **Projects**: `src/sync/projects.ts` - Project sync with progress tracking and IDN naming
|
|
64
|
+
- **Push Operations**: `src/sync/push.ts` - File validation and upload with multiple file detection
|
|
65
|
+
- **Status Checking**: `src/sync/status.ts` - Change detection with file validation warnings
|
|
66
|
+
- **Attributes**: `src/sync/attributes.ts` - Customer attributes synchronization
|
|
67
|
+
- **Conversations**: `src/sync/conversations.ts` - Conversation history management
|
|
68
|
+
- **Metadata**: `src/sync/metadata.ts` - flows.yaml generation (clean, no prompt_script)
|
|
69
|
+
- **File Utilities**: `src/sync/skill-files.ts` - IDN-based naming and validation
|
|
70
|
+
|
|
71
|
+
### šÆ Enhanced User Experience
|
|
72
|
+
|
|
73
|
+
**Progress Tracking:**
|
|
74
|
+
- Real-time progress display during large operations (966+ skills)
|
|
75
|
+
- Percentage completion with skill counts
|
|
76
|
+
- Clean progress updates every 10 processed skills
|
|
77
|
+
- Clear completion confirmations
|
|
78
|
+
|
|
79
|
+
**IDN-Based File Management:**
|
|
80
|
+
- Skills named after their IDN for easy identification
|
|
81
|
+
- Flexible file discovery (any .jinja/.guidance/.nsl file)
|
|
82
|
+
- Smart overwrite detection (content-based, not filename-based)
|
|
83
|
+
- Force mode with `--force/-f` flag for silent operations
|
|
84
|
+
|
|
85
|
+
**Enhanced Validation:**
|
|
86
|
+
- Multiple file detection with clear warnings
|
|
87
|
+
- Safe push operations that skip problematic skills
|
|
88
|
+
- Actionable error messages with resolution guidance
|
|
89
|
+
- Clean status display with file-level change detection
|
|
90
|
+
|
|
91
|
+
### š§ Technical Excellence
|
|
92
|
+
|
|
93
|
+
**Hash Consistency:**
|
|
94
|
+
- Complete hash coverage for flows.yaml and attributes.yaml
|
|
95
|
+
- No false positive change detection
|
|
96
|
+
- Status shows "Clean." immediately after pull operations
|
|
97
|
+
- Consistent hash tracking across all generated files
|
|
98
|
+
|
|
99
|
+
**File Structure Optimization:**
|
|
100
|
+
- flows.yaml cleanup (removed prompt_script content)
|
|
101
|
+
- Automatic attributes.yaml generation during pull
|
|
102
|
+
- Complete metadata.yaml hierarchy at all levels
|
|
103
|
+
- IDN-based skill folder organization
|
|
104
|
+
|
|
105
|
+
**Performance & Quality:**
|
|
106
|
+
- Zero TypeScript compilation errors with strict typing
|
|
107
|
+
- Modular loading with reduced memory footprint
|
|
108
|
+
- Efficient API calls and file operations
|
|
109
|
+
- Professional error handling and user guidance
|
|
110
|
+
|
|
111
|
+
### š Comprehensive Testing Results
|
|
112
|
+
|
|
113
|
+
**All Functions Validated:**
|
|
114
|
+
- ā
Pull: 966/944 skills processed with perfect progress tracking
|
|
115
|
+
- ā
Status: Accurate change detection and clean state validation
|
|
116
|
+
- ā
Push: Successful uploads with smart file validation
|
|
117
|
+
- ā
Multi-customer: Independent customer operations working correctly
|
|
118
|
+
- ā
Attributes: Automatic 230KB+ attributes.yaml generation
|
|
119
|
+
- ā
Conversations: Working conversation history extraction
|
|
120
|
+
- ā
AKB Import: Proper parsing and validation (2 articles parsed)
|
|
121
|
+
- ā
Error Handling: Clear, actionable messages for all scenarios
|
|
122
|
+
- ā
File Validation: Multiple file warnings and safe skipping
|
|
123
|
+
|
|
124
|
+
## [1.9.2] - 2025-09-16
|
|
125
|
+
|
|
126
|
+
### Fixed
|
|
127
|
+
- **Pagination Safety**: Prevent infinite loops and hanging during conversation processing
|
|
128
|
+
- Added max pages limit (50 pages = 5000 acts per persona) to prevent infinite pagination
|
|
129
|
+
- Improved persona filtering for voice actors to avoid unnecessary API calls
|
|
130
|
+
- Better error handling and verbose logging for debugging conversation processing
|
|
131
|
+
- Graceful handling of personas without voice actors (adds with empty acts array)
|
|
132
|
+
|
|
133
|
+
### Enhanced
|
|
134
|
+
- **Performance Optimization**: Faster conversation processing with early exits
|
|
135
|
+
- Skip personas without newo_voice actors immediately
|
|
136
|
+
- Clear verbose logging for pagination progress
|
|
137
|
+
- Optimized control flow to prevent hanging during large conversation extraction
|
|
138
|
+
|
|
8
139
|
## [1.9.1] - 2025-09-16
|
|
9
140
|
|
|
10
141
|
### Fixed
|
package/README.md
CHANGED
|
@@ -5,16 +5,20 @@
|
|
|
5
5
|
[](https://www.typescriptlang.org/)
|
|
6
6
|
[](https://nodejs.org/)
|
|
7
7
|
|
|
8
|
-
**NEWO CLI** -
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- š **
|
|
12
|
-
-
|
|
13
|
-
-
|
|
8
|
+
**NEWO CLI** - Professional command-line tool for NEWO AI Agent development. Features **modular architecture**, **IDN-based file management**, and **comprehensive multi-customer support**.
|
|
9
|
+
|
|
10
|
+
Sync NEWO "Project ā Agent ā Flow ā Skills" structure to local files with:
|
|
11
|
+
- š **Intelligent synchronization** - Pull projects, attributes, and conversations automatically
|
|
12
|
+
- šÆ **IDN-based naming** - Skills named as `{skillIdn}.jinja/.guidance` for better organization
|
|
13
|
+
- š **Real-time progress** - Live progress tracking during large operations (966+ skills)
|
|
14
|
+
- š¢ **Multi-customer workspaces** - Work with multiple NEWO accounts simultaneously
|
|
15
|
+
- š **Hierarchical structure** - Complete project metadata and organized file structure
|
|
14
16
|
- š **Secure authentication** - API key-based auth with automatic token refresh
|
|
15
|
-
- ā” **
|
|
17
|
+
- ā” **Smart change detection** - SHA256-based efficient sync with hash consistency
|
|
18
|
+
- š”ļø **File validation** - Multiple file detection with clear warnings and safe handling
|
|
16
19
|
- š§ **AI skill formats** - Support for `.guidance` (AI prompts) and `.jinja` (NSL templates)
|
|
17
20
|
- š **Knowledge base import** - Bulk import AKB articles from structured text files
|
|
21
|
+
- š¬ **Conversation history** - Extract and sync user conversations and personas
|
|
18
22
|
- š§ **CI/CD ready** - GitHub Actions integration for automated deployments
|
|
19
23
|
|
|
20
24
|
---
|
|
@@ -125,14 +129,15 @@ NEWO_REFRESH_URL=custom_refresh_endpoint # Custom refresh endpoint
|
|
|
125
129
|
|
|
126
130
|
### Core Commands
|
|
127
131
|
|
|
128
|
-
| Command | Description |
|
|
132
|
+
| Command | Description | Features |
|
|
129
133
|
|---------|-------------|----------|
|
|
130
|
-
| `newo pull` | Download projects
|
|
131
|
-
| `newo push` | Upload local changes to NEWO |
|
|
132
|
-
| `newo status` | Show modified files |
|
|
133
|
-
| `newo
|
|
134
|
-
| `newo
|
|
135
|
-
| `newo
|
|
134
|
+
| `newo pull` | Download projects + attributes + metadata | ⢠Real-time progress tracking (966+ skills)<br>⢠IDN-based file naming<br>⢠Automatic attributes.yaml generation<br>⢠`--force` for silent overwrite |
|
|
135
|
+
| `newo push` | Upload local changes to NEWO | ⢠Smart file validation<br>⢠Multiple file detection<br>⢠Hash-based change detection<br>⢠Safe error handling |
|
|
136
|
+
| `newo status` | Show modified files with details | ⢠Multiple file warnings<br>⢠Detailed change analysis<br>⢠Clean state validation<br>⢠Per-customer status |
|
|
137
|
+
| `newo conversations` | Pull conversation history | ⢠User personas and chat history<br>⢠YAML format output<br>⢠Pagination support |
|
|
138
|
+
| `newo list-customers` | List configured customers | ⢠Shows default customer<br>⢠Multi-customer discovery |
|
|
139
|
+
| `newo import-akb` | Import knowledge base articles | ⢠Structured text parsing<br>⢠Bulk article import<br>⢠Validation and error reporting |
|
|
140
|
+
| `newo meta` | Get project metadata (debug) | ⢠Project structure analysis<br>⢠Metadata validation |
|
|
136
141
|
|
|
137
142
|
### Multi-Customer Commands
|
|
138
143
|
|
|
@@ -168,20 +173,33 @@ newo push # Pushes to appropriate customers based on file origin
|
|
|
168
173
|
```
|
|
169
174
|
newo_customers/ # Root folder for all customers
|
|
170
175
|
āāā NEWO_ABC123/ # Customer folder (auto-detected IDN)
|
|
176
|
+
ā āāā attributes.yaml # Customer attributes (auto-generated)
|
|
177
|
+
ā āāā conversations.yaml # Conversation history (optional)
|
|
171
178
|
ā āāā projects/ # Customer's projects
|
|
172
|
-
ā āāā flows.yaml #
|
|
179
|
+
ā āāā flows.yaml # Clean metadata export (no prompt_script)
|
|
173
180
|
ā āāā ProjectAlpha/ # Individual project folder
|
|
174
|
-
ā āāā metadata.
|
|
181
|
+
ā āāā metadata.yaml # Project metadata
|
|
175
182
|
ā āāā agent_support/ # Agent folder
|
|
183
|
+
ā ā āāā metadata.yaml # Agent metadata
|
|
176
184
|
ā ā āāā flow_onboarding/ # Flow folder
|
|
177
|
-
ā ā ā āāā
|
|
178
|
-
ā ā ā
|
|
185
|
+
ā ā ā āāā metadata.yaml # Flow metadata
|
|
186
|
+
ā ā ā āāā skill_welcome/ # Skill folder (IDN-based)
|
|
187
|
+
ā ā ā ā āāā skill_welcome.guidance # IDN-named script
|
|
188
|
+
ā ā ā ā āāā metadata.yaml # Skill metadata
|
|
189
|
+
ā ā ā āāā skill_setup/ # Another skill folder
|
|
190
|
+
ā ā ā āāā skill_setup.jinja # IDN-named script
|
|
191
|
+
ā ā ā āāā metadata.yaml # Skill metadata
|
|
179
192
|
ā ā āāā flow_help/
|
|
180
|
-
ā ā āāā skill_faq
|
|
193
|
+
ā ā āāā skill_faq/
|
|
194
|
+
ā ā āāā skill_faq.guidance
|
|
195
|
+
ā ā āāā metadata.yaml
|
|
181
196
|
ā āāā agent_sales/
|
|
182
197
|
ā āāā flow_demo/
|
|
183
|
-
ā āāā skill_pitch
|
|
198
|
+
ā āāā skill_pitch/
|
|
199
|
+
ā āāā skill_pitch.jinja
|
|
200
|
+
ā āāā metadata.yaml
|
|
184
201
|
āāā NEWO_XYZ789/ # Another customer
|
|
202
|
+
ā āāā attributes.yaml
|
|
185
203
|
ā āāā projects/
|
|
186
204
|
ā āāā flows.yaml
|
|
187
205
|
ā āāā ProjectBeta/
|
|
@@ -282,6 +300,36 @@ When errors occur, NEWO CLI provides:
|
|
|
282
300
|
|
|
283
301
|
---
|
|
284
302
|
|
|
303
|
+
## šļø Modular Architecture (v2.0+)
|
|
304
|
+
|
|
305
|
+
**Professional modular design** for maintainability and extensibility:
|
|
306
|
+
|
|
307
|
+
### CLI Modules (`src/cli/`)
|
|
308
|
+
- **`cli.ts`** - Main entry point with command routing (124 lines)
|
|
309
|
+
- **`errors.ts`** - Centralized error handling with user-friendly messages
|
|
310
|
+
- **`customer-selection.ts`** - Customer management and selection logic
|
|
311
|
+
- **`commands/`** - Individual command handlers:
|
|
312
|
+
- `pull.ts`, `push.ts`, `status.ts`, `conversations.ts`
|
|
313
|
+
- `meta.ts`, `import-akb.ts`, `help.ts`, `list-customers.ts`
|
|
314
|
+
|
|
315
|
+
### Sync Modules (`src/sync/`)
|
|
316
|
+
- **`sync.ts`** - Unified exports and entry point (13 lines)
|
|
317
|
+
- **`projects.ts`** - Project sync operations with progress tracking
|
|
318
|
+
- **`push.ts`** - Push operations with file validation
|
|
319
|
+
- **`status.ts`** - Status checking with multiple file warnings
|
|
320
|
+
- **`attributes.ts`** - Customer attributes synchronization
|
|
321
|
+
- **`conversations.ts`** - Conversation history management
|
|
322
|
+
- **`metadata.ts`** - flows.yaml generation (clean, no prompt_script)
|
|
323
|
+
- **`skill-files.ts`** - File validation and IDN-based naming utilities
|
|
324
|
+
|
|
325
|
+
### Architecture Benefits
|
|
326
|
+
- **Single Responsibility** - Each module handles one specific domain
|
|
327
|
+
- **Enhanced Testability** - Independent modules with clear interfaces
|
|
328
|
+
- **Better Maintainability** - Easy to locate and modify functionality
|
|
329
|
+
- **Future-Proof** - Simple to add new commands and sync operations
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
285
333
|
## CI/CD Integration
|
|
286
334
|
|
|
287
335
|
### Single Customer CI/CD
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversations command handler
|
|
3
|
+
*/
|
|
4
|
+
import { makeClient } from '../../api.js';
|
|
5
|
+
import { pullConversations } from '../../sync.js';
|
|
6
|
+
import { getValidAccessToken } from '../../auth.js';
|
|
7
|
+
import { selectSingleCustomer } from '../customer-selection.js';
|
|
8
|
+
export async function handleConversationsCommand(customerConfig, args, verbose) {
|
|
9
|
+
const { selectedCustomer, allCustomers, isMultiCustomer } = selectSingleCustomer(customerConfig, args.customer);
|
|
10
|
+
// Parse conversation-specific options - load all data by default
|
|
11
|
+
const conversationOptions = {
|
|
12
|
+
includeAll: true, // Always include all data for conversations
|
|
13
|
+
maxPersonas: undefined, // No limit on personas
|
|
14
|
+
maxActsPerPersona: undefined // No limit on acts per persona
|
|
15
|
+
};
|
|
16
|
+
if (selectedCustomer) {
|
|
17
|
+
// Single customer conversations
|
|
18
|
+
const accessToken = await getValidAccessToken(selectedCustomer);
|
|
19
|
+
const client = await makeClient(verbose, accessToken);
|
|
20
|
+
console.log(`š¬ Pulling conversations for customer: ${selectedCustomer.idn} (all data)`);
|
|
21
|
+
await pullConversations(client, selectedCustomer, conversationOptions, verbose);
|
|
22
|
+
console.log(`ā
Conversations saved to newo_customers/${selectedCustomer.idn}/conversations.yaml`);
|
|
23
|
+
}
|
|
24
|
+
else if (isMultiCustomer) {
|
|
25
|
+
// Multi-customer conversations
|
|
26
|
+
if (verbose)
|
|
27
|
+
console.log(`š¬ No default customer specified, pulling conversations from all ${allCustomers.length} customers`);
|
|
28
|
+
console.log(`š¬ Pulling conversations from ${allCustomers.length} customers (all data)...`);
|
|
29
|
+
for (const customer of allCustomers) {
|
|
30
|
+
console.log(`\nš¬ Pulling conversations for customer: ${customer.idn}`);
|
|
31
|
+
const accessToken = await getValidAccessToken(customer);
|
|
32
|
+
const client = await makeClient(verbose, accessToken);
|
|
33
|
+
await pullConversations(client, customer, conversationOptions, verbose);
|
|
34
|
+
}
|
|
35
|
+
console.log(`\nā
Conversations pull completed for all ${allCustomers.length} customers`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=conversations.js.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Help command handler
|
|
3
|
+
*/
|
|
4
|
+
export function handleHelpCommand() {
|
|
5
|
+
console.log(`NEWO CLI - Multi-Customer Support
|
|
6
|
+
Usage:
|
|
7
|
+
newo pull [--customer <idn>] # download projects -> ./newo_customers/<idn>/projects/
|
|
8
|
+
newo push [--customer <idn>] # upload modified *.guidance/*.jinja back to NEWO
|
|
9
|
+
newo status [--customer <idn>] # show modified files
|
|
10
|
+
newo conversations [--customer <idn>] [--all] # download user conversations -> ./newo_customers/<idn>/conversations.yaml
|
|
11
|
+
newo list-customers # list available customers
|
|
12
|
+
newo meta [--customer <idn>] # get project metadata (debug)
|
|
13
|
+
newo import-akb <file> <persona_id> [--customer <idn>] # import AKB articles from file
|
|
14
|
+
|
|
15
|
+
Flags:
|
|
16
|
+
--customer <idn> # specify customer (if not set, uses default or interactive selection)
|
|
17
|
+
--all # include all available data (for conversations: all personas and acts)
|
|
18
|
+
--force, -f # force overwrite without prompting (for pull command)
|
|
19
|
+
--verbose, -v # enable detailed logging
|
|
20
|
+
|
|
21
|
+
Environment Variables:
|
|
22
|
+
NEWO_BASE_URL # NEWO API base URL (default: https://app.newo.ai)
|
|
23
|
+
NEWO_CUSTOMER_<IDN>_API_KEY # API key for customer <IDN>
|
|
24
|
+
NEWO_CUSTOMER_<IDN>_PROJECT_ID # Optional: specific project ID for customer
|
|
25
|
+
NEWO_DEFAULT_CUSTOMER # Optional: default customer to use
|
|
26
|
+
|
|
27
|
+
Multi-Customer Examples:
|
|
28
|
+
# Configure customers in .env:
|
|
29
|
+
NEWO_CUSTOMER_acme_API_KEY=your_acme_api_key
|
|
30
|
+
NEWO_CUSTOMER_globex_API_KEY=your_globex_api_key
|
|
31
|
+
NEWO_DEFAULT_CUSTOMER=acme
|
|
32
|
+
|
|
33
|
+
# Commands:
|
|
34
|
+
newo pull # Pull from all customers (if no default set)
|
|
35
|
+
newo pull --customer acme # Pull projects for Acme only
|
|
36
|
+
newo status # Status for all customers (if no default set)
|
|
37
|
+
newo push # Interactive selection for multiple customers
|
|
38
|
+
newo push --customer globex # Push changes for Globex only
|
|
39
|
+
|
|
40
|
+
File Structure:
|
|
41
|
+
newo_customers/
|
|
42
|
+
āāā acme/
|
|
43
|
+
ā āāā projects/
|
|
44
|
+
ā āāā project1/
|
|
45
|
+
āāā globex/
|
|
46
|
+
āāā projects/
|
|
47
|
+
āāā project2/
|
|
48
|
+
`);
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=help.js.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import AKB command handler
|
|
3
|
+
*/
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { makeClient, importAkbArticle } from '../../api.js';
|
|
6
|
+
import { parseAkbFile, prepareArticlesForImport } from '../../akb.js';
|
|
7
|
+
import { getValidAccessToken } from '../../auth.js';
|
|
8
|
+
import { requireSingleCustomer } from '../customer-selection.js';
|
|
9
|
+
export async function handleImportAkbCommand(customerConfig, args, verbose) {
|
|
10
|
+
const selectedCustomer = requireSingleCustomer(customerConfig, args.customer);
|
|
11
|
+
const akbFile = args._[1];
|
|
12
|
+
const personaId = args._[2];
|
|
13
|
+
if (!akbFile || !personaId) {
|
|
14
|
+
console.error('Usage: newo import-akb <file> <persona_id>');
|
|
15
|
+
console.error('Example: newo import-akb akb.txt da4550db-2b95-4500-91ff-fb4b60fe7be9');
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
const filePath = path.resolve(akbFile);
|
|
19
|
+
const accessToken = await getValidAccessToken(selectedCustomer);
|
|
20
|
+
const client = await makeClient(verbose, accessToken);
|
|
21
|
+
try {
|
|
22
|
+
if (verbose)
|
|
23
|
+
console.log(`š Parsing AKB file: ${filePath}`);
|
|
24
|
+
const articles = await parseAkbFile(filePath);
|
|
25
|
+
console.log(`ā Parsed ${articles.length} articles from ${akbFile}`);
|
|
26
|
+
if (verbose)
|
|
27
|
+
console.log(`š§ Preparing articles for persona: ${personaId}`);
|
|
28
|
+
const preparedArticles = prepareArticlesForImport(articles, personaId);
|
|
29
|
+
let successCount = 0;
|
|
30
|
+
let errorCount = 0;
|
|
31
|
+
console.log(`š¤ Importing ${preparedArticles.length} articles...`);
|
|
32
|
+
for (const [index, article] of preparedArticles.entries()) {
|
|
33
|
+
try {
|
|
34
|
+
if (verbose) {
|
|
35
|
+
console.log(` [${index + 1}/${preparedArticles.length}] Importing ${article.topic_name}...`);
|
|
36
|
+
}
|
|
37
|
+
await importAkbArticle(client, article);
|
|
38
|
+
successCount++;
|
|
39
|
+
if (!verbose)
|
|
40
|
+
process.stdout.write('.');
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
errorCount++;
|
|
44
|
+
const errorMessage = error instanceof Error && 'response' in error
|
|
45
|
+
? error?.response?.data
|
|
46
|
+
: error instanceof Error
|
|
47
|
+
? error.message
|
|
48
|
+
: String(error);
|
|
49
|
+
console.error(`\nā Failed to import ${article.topic_name}:`, errorMessage);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (!verbose)
|
|
53
|
+
console.log(''); // new line after dots
|
|
54
|
+
console.log(`ā
Import complete: ${successCount} successful, ${errorCount} failed`);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
58
|
+
console.error('ā AKB import failed:', message);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=import-akb.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List customers command handler
|
|
3
|
+
*/
|
|
4
|
+
import { listCustomers } from '../../customerAsync.js';
|
|
5
|
+
export function handleListCustomersCommand(customerConfig) {
|
|
6
|
+
const customers = listCustomers(customerConfig);
|
|
7
|
+
console.log('Available customers:');
|
|
8
|
+
for (const customerIdn of customers) {
|
|
9
|
+
const isDefault = customerConfig.defaultCustomer === customerIdn;
|
|
10
|
+
console.log(` ${customerIdn}${isDefault ? ' (default)' : ''}`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=list-customers.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meta command handler - get project metadata
|
|
3
|
+
*/
|
|
4
|
+
import { makeClient, getProjectMeta } from '../../api.js';
|
|
5
|
+
import { getValidAccessToken } from '../../auth.js';
|
|
6
|
+
import { requireSingleCustomer } from '../customer-selection.js';
|
|
7
|
+
export async function handleMetaCommand(customerConfig, args, verbose) {
|
|
8
|
+
const selectedCustomer = requireSingleCustomer(customerConfig, args.customer);
|
|
9
|
+
if (!selectedCustomer.projectId) {
|
|
10
|
+
console.error(`No project ID configured for customer ${selectedCustomer.idn}`);
|
|
11
|
+
console.error(`Set NEWO_CUSTOMER_${selectedCustomer.idn.toUpperCase()}_PROJECT_ID in your .env file`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
const accessToken = await getValidAccessToken(selectedCustomer);
|
|
15
|
+
const client = await makeClient(verbose, accessToken);
|
|
16
|
+
const meta = await getProjectMeta(client, selectedCustomer.projectId);
|
|
17
|
+
console.log(JSON.stringify(meta, null, 2));
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=meta.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pull attributes command handler
|
|
3
|
+
*/
|
|
4
|
+
import { makeClient } from '../../api.js';
|
|
5
|
+
import { saveCustomerAttributes } from '../../sync.js';
|
|
6
|
+
import { getValidAccessToken } from '../../auth.js';
|
|
7
|
+
import { requireSingleCustomer } from '../customer-selection.js';
|
|
8
|
+
export async function handlePullAttributesCommand(customerConfig, args, verbose) {
|
|
9
|
+
const selectedCustomer = requireSingleCustomer(customerConfig, args.customer);
|
|
10
|
+
const accessToken = await getValidAccessToken(selectedCustomer);
|
|
11
|
+
const client = await makeClient(verbose, accessToken);
|
|
12
|
+
console.log(`š Fetching customer attributes for ${selectedCustomer.idn}...`);
|
|
13
|
+
await saveCustomerAttributes(client, selectedCustomer, verbose);
|
|
14
|
+
console.log(`ā
Customer attributes saved to newo_customers/${selectedCustomer.idn}/attributes.yaml`);
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=pull-attributes.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pull command handler
|
|
3
|
+
*/
|
|
4
|
+
import { makeClient } from '../../api.js';
|
|
5
|
+
import { pullAll } from '../../sync.js';
|
|
6
|
+
import { getValidAccessToken } from '../../auth.js';
|
|
7
|
+
import { selectSingleCustomer } from '../customer-selection.js';
|
|
8
|
+
export async function handlePullCommand(customerConfig, args, verbose) {
|
|
9
|
+
const { selectedCustomer, allCustomers, isMultiCustomer } = selectSingleCustomer(customerConfig, args.customer);
|
|
10
|
+
// Check for force/silent overwrite flag
|
|
11
|
+
const silentOverwrite = Boolean(args.force || args.f);
|
|
12
|
+
if (selectedCustomer) {
|
|
13
|
+
// Single customer pull
|
|
14
|
+
const accessToken = await getValidAccessToken(selectedCustomer);
|
|
15
|
+
const client = await makeClient(verbose, accessToken);
|
|
16
|
+
const projectId = selectedCustomer.projectId || null;
|
|
17
|
+
await pullAll(client, selectedCustomer, projectId, verbose, silentOverwrite);
|
|
18
|
+
}
|
|
19
|
+
else if (isMultiCustomer) {
|
|
20
|
+
// Multi-customer pull
|
|
21
|
+
if (verbose)
|
|
22
|
+
console.log(`š„ No default customer specified, pulling from all ${allCustomers.length} customers`);
|
|
23
|
+
console.log(`š Pulling from ${allCustomers.length} customers...`);
|
|
24
|
+
for (const customer of allCustomers) {
|
|
25
|
+
console.log(`\nš„ Pulling from customer: ${customer.idn}`);
|
|
26
|
+
const accessToken = await getValidAccessToken(customer);
|
|
27
|
+
const client = await makeClient(verbose, accessToken);
|
|
28
|
+
const projectId = customer.projectId || null;
|
|
29
|
+
await pullAll(client, customer, projectId, verbose, silentOverwrite);
|
|
30
|
+
}
|
|
31
|
+
console.log(`\nā
Pull completed for all ${allCustomers.length} customers`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=pull.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Push command handler
|
|
3
|
+
*/
|
|
4
|
+
import { makeClient } from '../../api.js';
|
|
5
|
+
import { pushChanged } from '../../sync.js';
|
|
6
|
+
import { getValidAccessToken } from '../../auth.js';
|
|
7
|
+
import { selectSingleCustomer, interactiveCustomerSelection } from '../customer-selection.js';
|
|
8
|
+
export async function handlePushCommand(customerConfig, args, verbose) {
|
|
9
|
+
const { selectedCustomer, allCustomers, isMultiCustomer } = selectSingleCustomer(customerConfig, args.customer);
|
|
10
|
+
if (selectedCustomer) {
|
|
11
|
+
// Single customer push
|
|
12
|
+
const accessToken = await getValidAccessToken(selectedCustomer);
|
|
13
|
+
const client = await makeClient(verbose, accessToken);
|
|
14
|
+
await pushChanged(client, selectedCustomer, verbose);
|
|
15
|
+
}
|
|
16
|
+
else if (isMultiCustomer) {
|
|
17
|
+
// Multiple customers exist with no default, ask user
|
|
18
|
+
const customersToProcess = await interactiveCustomerSelection(allCustomers);
|
|
19
|
+
if (customersToProcess.length === 1) {
|
|
20
|
+
// Single customer selected
|
|
21
|
+
const customer = customersToProcess[0];
|
|
22
|
+
const accessToken = await getValidAccessToken(customer);
|
|
23
|
+
const client = await makeClient(verbose, accessToken);
|
|
24
|
+
await pushChanged(client, customer, verbose);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
// Multi-customer push (user selected "All customers")
|
|
28
|
+
console.log(`š Pushing to ${customersToProcess.length} customers...`);
|
|
29
|
+
for (const customer of customersToProcess) {
|
|
30
|
+
console.log(`\nš¤ Pushing for customer: ${customer.idn}`);
|
|
31
|
+
const accessToken = await getValidAccessToken(customer);
|
|
32
|
+
const client = await makeClient(verbose, accessToken);
|
|
33
|
+
await pushChanged(client, customer, verbose);
|
|
34
|
+
}
|
|
35
|
+
console.log(`\nā
Push completed for all ${customersToProcess.length} customers`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=push.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status command handler
|
|
3
|
+
*/
|
|
4
|
+
import { status } from '../../sync.js';
|
|
5
|
+
import { selectSingleCustomer } from '../customer-selection.js';
|
|
6
|
+
export async function handleStatusCommand(customerConfig, args, verbose) {
|
|
7
|
+
const { selectedCustomer, allCustomers, isMultiCustomer } = selectSingleCustomer(customerConfig, args.customer);
|
|
8
|
+
if (selectedCustomer) {
|
|
9
|
+
// Single customer status
|
|
10
|
+
await status(selectedCustomer, verbose);
|
|
11
|
+
}
|
|
12
|
+
else if (isMultiCustomer) {
|
|
13
|
+
// Multi-customer status
|
|
14
|
+
console.log(`š Checking status for ${allCustomers.length} customers...`);
|
|
15
|
+
for (const customer of allCustomers) {
|
|
16
|
+
console.log(`\nš Status for customer: ${customer.idn}`);
|
|
17
|
+
await status(customer, verbose);
|
|
18
|
+
}
|
|
19
|
+
console.log(`\nā
Status check completed for all ${allCustomers.length} customers`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { CustomerConfig, MultiCustomerConfig } from '../types.js';
|
|
2
|
+
export interface CustomerSelectionResult {
|
|
3
|
+
selectedCustomer: CustomerConfig | null;
|
|
4
|
+
allCustomers: CustomerConfig[];
|
|
5
|
+
isMultiCustomer: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Parse and validate customer configuration
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseAndValidateCustomerConfig(env: any, verbose: boolean): Promise<MultiCustomerConfig>;
|
|
11
|
+
/**
|
|
12
|
+
* Handle customer selection for commands that support single customer operations
|
|
13
|
+
*/
|
|
14
|
+
export declare function selectSingleCustomer(customerConfig: MultiCustomerConfig, customerArg?: string): CustomerSelectionResult;
|
|
15
|
+
/**
|
|
16
|
+
* Handle customer selection for commands that require exactly one customer
|
|
17
|
+
*/
|
|
18
|
+
export declare function requireSingleCustomer(customerConfig: MultiCustomerConfig, customerArg?: string): CustomerConfig;
|
|
19
|
+
/**
|
|
20
|
+
* Interactive customer selection for commands like push
|
|
21
|
+
*/
|
|
22
|
+
export declare function interactiveCustomerSelection(allCustomers: CustomerConfig[]): Promise<CustomerConfig[]>;
|
|
23
|
+
//# sourceMappingURL=customer-selection.d.ts.map
|