newo 3.4.2 ā 3.6.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.
- package/.env.example +5 -0
- package/CHANGELOG.md +27 -0
- package/README.md +27 -9
- package/dist/api.d.ts +18 -0
- package/dist/api.js +28 -0
- package/dist/cli/commands/export.d.ts +3 -0
- package/dist/cli/commands/export.js +62 -0
- package/dist/cli/commands/help.js +54 -42
- package/dist/cli/commands/pull.js +38 -14
- package/dist/cli/commands/push.js +32 -32
- package/dist/cli/commands/status.js +46 -7
- package/dist/cli-new/bootstrap.d.ts +7 -1
- package/dist/cli-new/bootstrap.js +11 -5
- package/dist/cli-new/di/tokens.d.ts +1 -0
- package/dist/cli-new/di/tokens.js +1 -0
- package/dist/cli.js +4 -0
- package/dist/domain/strategies/sync/ProjectSyncStrategy.d.ts +5 -0
- package/dist/domain/strategies/sync/ProjectSyncStrategy.js +97 -8
- package/dist/domain/strategies/sync/V2ProjectSyncStrategy.d.ts +80 -0
- package/dist/domain/strategies/sync/V2ProjectSyncStrategy.js +725 -0
- package/dist/env.d.ts +1 -0
- package/dist/env.js +1 -0
- package/dist/format/detect.d.ts +14 -0
- package/dist/format/detect.js +105 -0
- package/dist/format/extensions.d.ts +26 -0
- package/dist/format/extensions.js +45 -0
- package/dist/format/index.d.ts +11 -0
- package/dist/format/index.js +11 -0
- package/dist/format/paths-v2.d.ts +31 -0
- package/dist/format/paths-v2.js +104 -0
- package/dist/format/types.d.ts +28 -0
- package/dist/format/types.js +21 -0
- package/dist/format/v2-yaml.d.ts +143 -0
- package/dist/format/v2-yaml.js +222 -0
- package/dist/format/yaml-patch.d.ts +14 -0
- package/dist/format/yaml-patch.js +184 -0
- package/dist/fsutil.d.ts +10 -0
- package/dist/fsutil.js +25 -0
- package/dist/sync/attributes.js +3 -3
- package/dist/sync/skill-files.js +2 -2
- package/dist/types.d.ts +5 -0
- package/package.json +1 -1
- package/src/api.ts +64 -0
- package/src/cli/commands/export.ts +78 -0
- package/src/cli/commands/help.ts +54 -42
- package/src/cli/commands/pull.ts +46 -15
- package/src/cli/commands/push.ts +38 -31
- package/src/cli/commands/status.ts +59 -9
- package/src/cli-new/bootstrap.ts +19 -7
- package/src/cli-new/di/tokens.ts +1 -0
- package/src/cli.ts +5 -0
- package/src/domain/strategies/sync/ProjectSyncStrategy.ts +122 -8
- package/src/domain/strategies/sync/V2ProjectSyncStrategy.ts +1007 -0
- package/src/env.ts +2 -0
- package/src/format/detect.ts +123 -0
- package/src/format/extensions.ts +61 -0
- package/src/format/index.ts +66 -0
- package/src/format/paths-v2.ts +207 -0
- package/src/format/types.ts +40 -0
- package/src/format/v2-yaml.ts +345 -0
- package/src/format/yaml-patch.ts +208 -0
- package/src/fsutil.ts +37 -0
- package/src/sync/attributes.ts +3 -3
- package/src/sync/skill-files.ts +2 -2
- package/src/types.ts +6 -0
package/.env.example
CHANGED
|
@@ -19,6 +19,11 @@ NEWO_API_KEYS=["api_key_1", "api_key_2", "api_key_3"]
|
|
|
19
19
|
# NEWO_API_KEY=put_api_key_here
|
|
20
20
|
# NEWO_PROJECT_ID=b78188ba-0df0-46a8-8713-f0d7cff0a06e
|
|
21
21
|
|
|
22
|
+
# Project format (default: cli_v1, only affects NEW pulls where no local files exist)
|
|
23
|
+
# cli_v1 = Native CLI format (full feature support, per-entity metadata)
|
|
24
|
+
# newo_v2 = NEWO platform export format (compatible with platform UI exports)
|
|
25
|
+
# NEWO_FORMAT=cli_v1
|
|
26
|
+
|
|
22
27
|
# Optional bootstrap tokens (for legacy mode or manual setup)
|
|
23
28
|
NEWO_ACCESS_TOKEN=
|
|
24
29
|
NEWO_REFRESH_TOKEN=
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.6.1] - 2026-04-14
|
|
11
|
+
|
|
12
|
+
### Documentation
|
|
13
|
+
|
|
14
|
+
- Update README.md with V2 format, libraries, and `newo export` documentation for npm package page
|
|
15
|
+
|
|
16
|
+
## [3.6.0] - 2026-04-13
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **NEWO V2 Format Support** - Dual format support: `cli_v1` (native, default) and `newo_v2` (NEWO platform import/export compatible). Format is auto-detected per customer from filesystem (`import_version.txt` = newo_v2, `projects/` dir = cli_v1). Multiple formats coexist in the same workspace.
|
|
21
|
+
- **Libraries** - New entity type for shared reusable skills across agents within a project. Full pull/push/status support in both cli_v1 and newo_v2 formats via `GET/POST /api/v1/designer/projects/{id}/libraries` and `PATCH /api/v1/designer/libraries/{id}/skills/{id}`.
|
|
22
|
+
- **`newo export` command** - Bulk download V2 export ZIP from NEWO platform via `POST /api/v2/designer/customer/export`. Usage: `newo export [--output file.zip] [--customer idn]`.
|
|
23
|
+
- **`--format` flag** - Per-command format override for `pull`, `push`, and `status` commands. Values: `cli_v1`, `newo_v2`.
|
|
24
|
+
- **`NEWO_FORMAT` environment variable** - Persistent default format for new pulls (only affects customers without existing local files).
|
|
25
|
+
- **Format auto-detection** - Automatic per-customer format detection from filesystem markers. Existing projects always auto-detect; env var only applies to fresh pulls.
|
|
26
|
+
- **V2ProjectSyncStrategy** - New sync strategy using same V1 APIs but writing newo_v2 file layout (agents/flows/skills structure, .nsl/.nslg extensions, inline flow YAML with skills/events/states).
|
|
27
|
+
- **Format module** (`src/format/`) - Types, detection, extension mapping, V2 paths, V2 YAML parsers/generators, and pyyaml-compatible formatting.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- `pull`, `push`, and `status` commands now support `--format` flag and auto-detect format per customer
|
|
32
|
+
- DI bootstrap conditionally registers V2ProjectSyncStrategy when format is newo_v2
|
|
33
|
+
- `skill-files.ts` now recognizes `.nslg` extension alongside `.guidance`, `.jinja`, `.nsl`
|
|
34
|
+
- `env.ts` validates `NEWO_FORMAT` environment variable
|
|
35
|
+
- API client factory passes customer config to `getValidAccessToken()` for correct multi-customer auth
|
|
36
|
+
|
|
10
37
|
## [3.4.2] - 2026-04-12
|
|
11
38
|
|
|
12
39
|
### Added
|
package/README.md
CHANGED
|
@@ -8,22 +8,22 @@
|
|
|
8
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
9
|
|
|
10
10
|
Sync NEWO "Project ā Agent ā Flow ā Skills" structure to local files with:
|
|
11
|
+
- š **Dual format support** (v3.6.0) - `cli_v1` (native) and `newo_v2` (platform compatible), auto-detected per customer
|
|
12
|
+
- š **Libraries** (v3.6.0) - Pull/push shared reusable skills across agents within a project
|
|
13
|
+
- š **Bulk export** (v3.6.0) - `newo export` downloads complete V2 ZIP from platform
|
|
11
14
|
- š **Account migration** - Fully automated account copying with 100% accuracy
|
|
12
15
|
- šļø **Complete entity management** - Create, edit, and delete agents, flows, skills, events, and states
|
|
13
16
|
- š **Intelligent synchronization** - Pull projects, attributes, and conversations automatically
|
|
14
|
-
- šÆ **IDN-based naming** - Skills named as `{skillIdn}.jinja/.guidance`
|
|
17
|
+
- šÆ **IDN-based naming** - Skills named as `{skillIdn}.jinja/.guidance` or `{skillIdn}.nsl/.nslg`
|
|
15
18
|
- š **Real-time progress** - Live progress tracking during large operations (1,000+ skills)
|
|
16
19
|
- š¢ **Multi-customer workspaces** - Work with multiple NEWO accounts simultaneously
|
|
17
|
-
- š **Hierarchical structure** - Complete project metadata and organized file structure
|
|
18
20
|
- š **Secure authentication** - API key-based auth with automatic token refresh
|
|
19
21
|
- ā” **Smart change detection** - SHA256-based efficient sync with hash consistency
|
|
20
|
-
-
|
|
21
|
-
- š§ **AI skill formats** - Support for `.guidance` (AI prompts) and `.jinja` (NSL templates)
|
|
22
|
+
- š§ **AI skill formats** - Support for `.guidance`/`.jinja` (V1) and `.nslg`/`.nsl` (V2)
|
|
22
23
|
- š” **Webhook automation** - Automatic webhook creation from YAML configuration
|
|
23
24
|
- š **Knowledge base import** - Bulk import AKB articles from structured text files
|
|
24
25
|
- š¬ **Conversation history** - Extract and sync user conversations and personas
|
|
25
26
|
- š§Ŗ **Sandbox testing** - Interactive agent testing with conversation continuation
|
|
26
|
-
- ā
**Migration verification** - Automated validation of migration completeness
|
|
27
27
|
- š§ **CI/CD ready** - GitHub Actions integration for automated deployments
|
|
28
28
|
|
|
29
29
|
---
|
|
@@ -143,15 +143,33 @@ NEWO_REFRESH_URL=custom_refresh_endpoint # Custom refresh endpoint
|
|
|
143
143
|
|
|
144
144
|
| Command | Description | Features |
|
|
145
145
|
|---------|-------------|----------|
|
|
146
|
-
| `newo pull` | Download projects + attributes +
|
|
147
|
-
| `newo push` | Upload local changes to NEWO | ā¢
|
|
148
|
-
| `newo status` | Show modified files
|
|
149
|
-
| `newo
|
|
146
|
+
| `newo pull [--format <fmt>]` | Download projects + attributes + libraries | ⢠Dual format: `cli_v1` / `newo_v2`<br>⢠Auto-detects format per customer<br>⢠Real-time progress tracking (1,000+ skills)<br>⢠IDN-based file naming<br>⢠`--force` for silent overwrite |
|
|
147
|
+
| `newo push [--format <fmt>]` | Upload local changes to NEWO | ⢠Works with both formats<br>⢠Hash-based change detection<br>⢠Library skill updates<br>⢠Publishes flows automatically |
|
|
148
|
+
| `newo status [--format <fmt>]` | Show modified files | ⢠Format-aware status<br>⢠Multiple file warnings<br>⢠Per-customer status |
|
|
149
|
+
| `newo export [--output <file>]` | Download V2 bulk ZIP from platform | ⢠Complete organization export<br>⢠Projects, agents, flows, skills, attributes, AKB<br>⢠Compatible with platform UI import |
|
|
150
|
+
| `newo sandbox` | Test agents in sandbox chat mode | ⢠Single-command mode for automation<br>⢠Multi-turn conversation support<br>⢠Debug info for agent development |
|
|
150
151
|
| `newo conversations` | Pull conversation history | ⢠User personas and chat history<br>⢠YAML format output<br>⢠Pagination support |
|
|
151
152
|
| `newo list-customers` | List configured customers | ⢠Shows default customer<br>⢠Multi-customer discovery |
|
|
152
153
|
| `newo import-akb` | Import knowledge base articles | ⢠Structured text parsing<br>⢠Bulk article import<br>⢠Validation and error reporting |
|
|
153
154
|
| `newo meta` | Get project metadata (debug) | ⢠Project structure analysis<br>⢠Metadata validation |
|
|
154
155
|
|
|
156
|
+
### V2 Format Support (NEW v3.6.0)
|
|
157
|
+
|
|
158
|
+
The CLI supports two formats that coexist in the same workspace:
|
|
159
|
+
|
|
160
|
+
- **`cli_v1`** (default) - Native CLI format with per-entity metadata files, `.guidance`/`.jinja` extensions
|
|
161
|
+
- **`newo_v2`** - Platform import/export format, `.nslg`/`.nsl` extensions, compatible with SuperAgent repository and NEWO platform export ZIPs
|
|
162
|
+
|
|
163
|
+
**Format is auto-detected per customer** from filesystem markers (`import_version.txt` = V2, `projects/` dir = V1). Override per-command with `--format newo_v2` or set persistent default with `NEWO_FORMAT=newo_v2` in `.env`.
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
newo pull --format newo_v2 # Pull in V2 format
|
|
167
|
+
newo export --output backup.zip # Bulk V2 ZIP download
|
|
168
|
+
newo push --format newo_v2 # Push from V2 format project
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Round-trip tested against real SuperAgent export: 1426/1426 files, 1331 scripts byte-identical, zero data loss on platform import.
|
|
172
|
+
|
|
155
173
|
### Account Migration Commands
|
|
156
174
|
|
|
157
175
|
**Enterprise-grade account migration with 100% automation:**
|
package/dist/api.d.ts
CHANGED
|
@@ -90,4 +90,22 @@ export declare function listRegistryItems(client: AxiosInstance, registryId: str
|
|
|
90
90
|
export declare function addProjectFromRegistry(client: AxiosInstance, projectData: AddProjectFromRegistryRequest): Promise<CreateProjectResponse>;
|
|
91
91
|
export declare function createNewoCustomer(client: AxiosInstance, customerData: CreateNewoCustomerRequest): Promise<CreateNewoCustomerResponse>;
|
|
92
92
|
export declare function getLogs(client: AxiosInstance, params: LogsQueryParams): Promise<LogsResponse>;
|
|
93
|
+
export interface LibraryResponse {
|
|
94
|
+
id: string;
|
|
95
|
+
idn: string;
|
|
96
|
+
project_id: string;
|
|
97
|
+
skills: Skill[];
|
|
98
|
+
}
|
|
99
|
+
export declare function listLibraries(client: AxiosInstance, projectId: string): Promise<LibraryResponse[]>;
|
|
100
|
+
export declare function listLibrarySkills(client: AxiosInstance, libraryId: string): Promise<Skill[]>;
|
|
101
|
+
export declare function updateLibrarySkill(client: AxiosInstance, libraryId: string, skillId: string, data: {
|
|
102
|
+
prompt_script: string;
|
|
103
|
+
[key: string]: unknown;
|
|
104
|
+
}): Promise<void>;
|
|
105
|
+
export interface V2ExportOptions {
|
|
106
|
+
export_akb?: boolean;
|
|
107
|
+
export_customer_attributes?: boolean;
|
|
108
|
+
exclude_projects_idn?: string[];
|
|
109
|
+
}
|
|
110
|
+
export declare function exportCustomerV2(client: AxiosInstance, customerId: string, options?: V2ExportOptions): Promise<Buffer>;
|
|
93
111
|
//# sourceMappingURL=api.d.ts.map
|
package/dist/api.js
CHANGED
|
@@ -418,4 +418,32 @@ export async function getLogs(client, params) {
|
|
|
418
418
|
const response = await client.get(`/api/v1/analytics/logs?${queryParams.toString()}`);
|
|
419
419
|
return response.data;
|
|
420
420
|
}
|
|
421
|
+
export async function listLibraries(client, projectId) {
|
|
422
|
+
const response = await client.get(`/api/v1/designer/projects/${projectId}/libraries`);
|
|
423
|
+
return response.data;
|
|
424
|
+
}
|
|
425
|
+
export async function listLibrarySkills(client, libraryId) {
|
|
426
|
+
const response = await client.get(`/api/v1/designer/libraries/${libraryId}/skills`);
|
|
427
|
+
return response.data;
|
|
428
|
+
}
|
|
429
|
+
export async function updateLibrarySkill(client, libraryId, skillId, data) {
|
|
430
|
+
await client.patch(`/api/v1/designer/libraries/${libraryId}/skills/${skillId}`, data);
|
|
431
|
+
}
|
|
432
|
+
export async function exportCustomerV2(client, customerId, options = {}) {
|
|
433
|
+
const params = new URLSearchParams();
|
|
434
|
+
params.set('customer_id', customerId);
|
|
435
|
+
if (options.export_akb !== undefined)
|
|
436
|
+
params.set('export_akb', String(options.export_akb));
|
|
437
|
+
if (options.export_customer_attributes !== undefined)
|
|
438
|
+
params.set('export_customer_attributes', String(options.export_customer_attributes));
|
|
439
|
+
if (options.exclude_projects_idn && options.exclude_projects_idn.length > 0) {
|
|
440
|
+
for (const idn of options.exclude_projects_idn) {
|
|
441
|
+
params.append('exclude_projects_idn', idn);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
const response = await client.post(`/api/v2/designer/customer/export?${params.toString()}`, null, {
|
|
445
|
+
responseType: 'arraybuffer',
|
|
446
|
+
});
|
|
447
|
+
return Buffer.from(response.data);
|
|
448
|
+
}
|
|
421
449
|
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Export command handler - downloads V2 bulk export ZIP from platform
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* newo export # Export to temp/export-{customerIdn}-{timestamp}.zip
|
|
6
|
+
* newo export --output my-export.zip # Export to specific file
|
|
7
|
+
* newo export --no-akb # Exclude AKB from export
|
|
8
|
+
* newo export --no-attributes # Exclude customer attributes
|
|
9
|
+
*/
|
|
10
|
+
import { makeClient, exportCustomerV2 } from '../../api.js';
|
|
11
|
+
import { getValidAccessToken } from '../../auth.js';
|
|
12
|
+
import { selectSingleCustomer } from '../customer-selection.js';
|
|
13
|
+
import fs from 'fs-extra';
|
|
14
|
+
import path from 'path';
|
|
15
|
+
export async function handleExportCommand(customerConfig, args, verbose) {
|
|
16
|
+
const { selectedCustomer } = selectSingleCustomer(customerConfig, args.customer);
|
|
17
|
+
if (!selectedCustomer) {
|
|
18
|
+
console.error('Please specify a customer with --customer');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const accessToken = await getValidAccessToken(selectedCustomer);
|
|
22
|
+
const client = await makeClient(verbose, accessToken);
|
|
23
|
+
// Get customer ID from token (needed for V2 export API)
|
|
24
|
+
const customerId = await getCustomerIdFromToken(accessToken);
|
|
25
|
+
if (!customerId) {
|
|
26
|
+
console.error('Could not determine customer ID from token');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
const exportAkb = !args['no-akb'];
|
|
30
|
+
const exportAttributes = !args['no-attributes'];
|
|
31
|
+
console.log(`Downloading V2 export for customer ${selectedCustomer.idn}...`);
|
|
32
|
+
const zipBuffer = await exportCustomerV2(client, customerId, {
|
|
33
|
+
export_akb: exportAkb,
|
|
34
|
+
export_customer_attributes: exportAttributes,
|
|
35
|
+
});
|
|
36
|
+
// Determine output path
|
|
37
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
38
|
+
const defaultName = `export-${selectedCustomer.idn}-${timestamp}.zip`;
|
|
39
|
+
const outputPath = args.output || args.o
|
|
40
|
+
|| path.join(process.cwd(), 'temp', defaultName);
|
|
41
|
+
await fs.ensureDir(path.dirname(outputPath));
|
|
42
|
+
await fs.writeFile(outputPath, zipBuffer);
|
|
43
|
+
console.log(`Exported ${(zipBuffer.length / 1024 / 1024).toFixed(1)}MB to ${outputPath}`);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Extract customer_id from JWT token payload
|
|
47
|
+
*/
|
|
48
|
+
function getCustomerIdFromToken(token) {
|
|
49
|
+
try {
|
|
50
|
+
const parts = token.split('.');
|
|
51
|
+
if (parts.length < 2)
|
|
52
|
+
return null;
|
|
53
|
+
const payload = parts[1];
|
|
54
|
+
const padded = payload + '='.repeat(4 - (payload.length % 4));
|
|
55
|
+
const decoded = JSON.parse(Buffer.from(padded, 'base64').toString('utf8'));
|
|
56
|
+
return decoded['customer_id'] || decoded['sub'] || null;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=export.js.map
|
|
@@ -6,19 +6,20 @@ export function handleHelpCommand() {
|
|
|
6
6
|
A professional command-line tool for NEWO AI Agent development with modular architecture and comprehensive multi-customer support.
|
|
7
7
|
|
|
8
8
|
Core Commands:
|
|
9
|
-
newo pull [--customer <idn>]
|
|
10
|
-
newo push [--customer <idn>] [--
|
|
11
|
-
newo status [--customer <idn>]
|
|
12
|
-
newo
|
|
13
|
-
newo
|
|
14
|
-
newo
|
|
15
|
-
newo
|
|
9
|
+
newo pull [--customer <idn>] [--format <fmt>] # download projects + attributes + libraries
|
|
10
|
+
newo push [--customer <idn>] [--format <fmt>] # upload modified skills + attributes, publish flows
|
|
11
|
+
newo status [--customer <idn>] [--format <fmt>] # show modified files that would be pushed
|
|
12
|
+
newo export [--customer <idn>] [--output <file>] # download V2 bulk export ZIP from platform
|
|
13
|
+
newo watch [--customer <idn>] # watch for file changes and auto-push
|
|
14
|
+
newo diff [--customer <idn>] # show differences between local and remote
|
|
15
|
+
newo logs [--customer <idn>] # fetch and display analytics logs from platform
|
|
16
|
+
newo conversations [--customer <idn>] [--all] # download user conversations -> conversations.yaml
|
|
16
17
|
newo sandbox "<message>" [--customer <idn>] # test agent in sandbox - single message mode
|
|
17
|
-
newo sandbox --actor <id> "message" # continue existing sandbox conversation
|
|
18
|
+
newo sandbox --actor <id> "message" # continue existing sandbox conversation
|
|
18
19
|
newo pull-attributes [--customer <idn>] # download customer + project attributes
|
|
19
|
-
newo list-customers # list available customers
|
|
20
|
-
newo meta [--customer <idn>] # get project metadata (debug
|
|
21
|
-
newo import-akb <file> <persona_id> [--customer <idn>] # import AKB articles
|
|
20
|
+
newo list-customers # list available customers
|
|
21
|
+
newo meta [--customer <idn>] # get project metadata (debug)
|
|
22
|
+
newo import-akb <file> <persona_id> [--customer <idn>] # import AKB articles
|
|
22
23
|
|
|
23
24
|
Project Management:
|
|
24
25
|
newo create-project <idn> [--title <title>] [--description <desc>] [--version <version>] [--auto-update] # create empty project on platform
|
|
@@ -74,6 +75,7 @@ Account & Customer Management:
|
|
|
74
75
|
|
|
75
76
|
Flags:
|
|
76
77
|
--customer <idn> # specify customer (if not set, uses default or interactive selection)
|
|
78
|
+
--format <cli_v1|newo_v2> # project format (auto-detected from filesystem, or set NEWO_FORMAT in .env)
|
|
77
79
|
--all # include all available data (for conversations: all personas and acts)
|
|
78
80
|
--force, -f # force overwrite without prompting (for pull command)
|
|
79
81
|
--verbose, -v # enable detailed logging and progress information
|
|
@@ -81,6 +83,7 @@ Flags:
|
|
|
81
83
|
--actor <id> # continue existing sandbox chat with actor/chat ID
|
|
82
84
|
--confirm # confirm destructive operations without prompting
|
|
83
85
|
--no-publish # skip automatic flow publishing during push operations
|
|
86
|
+
--output, -o <file> # output file path (for export command)
|
|
84
87
|
|
|
85
88
|
Selective Sync Flags (NEW):
|
|
86
89
|
--only <resources> # sync only specified resources (comma-separated)
|
|
@@ -98,6 +101,7 @@ Selective Sync Flags (NEW):
|
|
|
98
101
|
|
|
99
102
|
Environment Variables:
|
|
100
103
|
NEWO_BASE_URL # NEWO API base URL (default: https://app.newo.ai)
|
|
104
|
+
NEWO_FORMAT # Default format for new pulls: cli_v1 (default) or newo_v2
|
|
101
105
|
|
|
102
106
|
Single Customer:
|
|
103
107
|
NEWO_API_KEY # API key for single customer setup
|
|
@@ -199,37 +203,45 @@ Usage Examples:
|
|
|
199
203
|
newo create-customer "Test Co" --email test@test.com --status temporal # Temporal (trial) customer
|
|
200
204
|
newo create-customer "Partner" --email p@p.com --project nac_integration --auto-update # With project template
|
|
201
205
|
|
|
202
|
-
File Structure:
|
|
203
|
-
newo_customers
|
|
204
|
-
āāā
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
206
|
+
File Structure (cli_v1 - default):
|
|
207
|
+
newo_customers/<customer-idn>/
|
|
208
|
+
āāā attributes.yaml # Customer attributes
|
|
209
|
+
āāā conversations.yaml # User conversations
|
|
210
|
+
āāā akb/<agent-idn>.yaml # AKB knowledge base
|
|
211
|
+
āāā integrations/ # Integration configurations
|
|
212
|
+
āāā projects/<project-idn>/
|
|
213
|
+
āāā metadata.yaml # Project metadata
|
|
214
|
+
āāā flows.yaml # Auto-generated structure
|
|
215
|
+
āāā attributes.yaml # Project attributes
|
|
216
|
+
āāā libraries/<lib-idn>/ # Shared skill libraries
|
|
217
|
+
ā āāā metadata.yaml
|
|
218
|
+
ā āāā <skill-idn>/<skill>.jinja|.guidance
|
|
219
|
+
āāā <agent-idn>/<flow-idn>/<skill-idn>/
|
|
220
|
+
āāā <skill>.guidance|.jinja # Skill scripts
|
|
221
|
+
āāā metadata.yaml # Skill metadata
|
|
222
|
+
|
|
223
|
+
File Structure (newo_v2 - platform compatible):
|
|
224
|
+
newo_customers/<customer-idn>/
|
|
225
|
+
āāā import_version.txt # Format marker (v2.0.0)
|
|
226
|
+
āāā attributes.yaml # Customer attributes
|
|
227
|
+
āāā akb/<agent-idn>.yaml # AKB knowledge base
|
|
228
|
+
āāā <project-idn>/
|
|
229
|
+
āāā <project-idn>.yaml # Project metadata
|
|
230
|
+
āāā attributes.yaml # Project attributes
|
|
231
|
+
āāā libraries/<lib-idn>/ # Shared skill libraries
|
|
232
|
+
ā āāā <lib-idn>.yaml # Library + inline skill defs
|
|
233
|
+
ā āāā skills/<skill>.nsl|.nslg
|
|
234
|
+
āāā agents/<agent-idn>/
|
|
235
|
+
āāā agent.yaml # Agent metadata
|
|
236
|
+
āāā flows/<flow-idn>/
|
|
237
|
+
āāā <flow-idn>.yaml # Flow + inline skill defs + events
|
|
238
|
+
āāā skills/<skill>.nsl|.nslg # Skill scripts
|
|
239
|
+
|
|
240
|
+
Format Detection:
|
|
241
|
+
Format is auto-detected per customer from the filesystem:
|
|
242
|
+
- import_version.txt present -> newo_v2
|
|
243
|
+
- projects/ directory present -> cli_v1
|
|
244
|
+
Set NEWO_FORMAT=newo_v2 in .env for new pulls, or use --format flag
|
|
233
245
|
|
|
234
246
|
For more information, visit: https://github.com/sabbah13/newo-cli
|
|
235
247
|
`);
|
|
@@ -14,6 +14,7 @@ import { getValidAccessToken } from '../../auth.js';
|
|
|
14
14
|
import { selectSingleCustomer } from '../customer-selection.js';
|
|
15
15
|
import { setupCli } from '../../cli-new/bootstrap.js';
|
|
16
16
|
import { ALL_RESOURCE_TYPES, RESOURCE_TYPES } from '../../cli-new/di/tokens.js';
|
|
17
|
+
import { resolveFormat } from '../../format/detect.js';
|
|
17
18
|
/**
|
|
18
19
|
* Parse resource list from comma-separated string
|
|
19
20
|
*/
|
|
@@ -40,10 +41,10 @@ function validateResources(resources) {
|
|
|
40
41
|
return { valid, invalid };
|
|
41
42
|
}
|
|
42
43
|
/**
|
|
43
|
-
* Pull using
|
|
44
|
+
* Pull using SyncEngine with selective sync and format support
|
|
44
45
|
*/
|
|
45
|
-
async function
|
|
46
|
-
const { syncEngine, logger } = setupCli(customerConfig, verbose);
|
|
46
|
+
async function pullWithSyncEngine(customerConfig, customer, resources, verbose, silentOverwrite, formatVersion) {
|
|
47
|
+
const { syncEngine, logger } = setupCli(customerConfig, verbose, formatVersion);
|
|
47
48
|
const pullOptions = {
|
|
48
49
|
verbose,
|
|
49
50
|
silentOverwrite
|
|
@@ -102,14 +103,27 @@ export async function handlePullCommand(customerConfig, args, verbose) {
|
|
|
102
103
|
resourcesToFetch = 'all';
|
|
103
104
|
console.log(`š¦ Pulling ALL resources`);
|
|
104
105
|
}
|
|
105
|
-
// Use
|
|
106
|
-
const
|
|
106
|
+
// Use SyncEngine if selective sync requested, otherwise use legacy for backward compatibility
|
|
107
|
+
const useSyncEngine = onlyResources.length > 0 || excludeResources.length > 0 || pullAllResources;
|
|
108
|
+
// Explicit --format flag (applies to all customers in this run)
|
|
109
|
+
const explicitFormat = args.format;
|
|
107
110
|
if (selectedCustomer) {
|
|
108
|
-
|
|
109
|
-
|
|
111
|
+
// Resolve format for this customer
|
|
112
|
+
const formatConfig = resolveFormat(selectedCustomer.idn, explicitFormat);
|
|
113
|
+
const isV2Format = formatConfig.version === 'newo_v2';
|
|
114
|
+
if (verbose || isV2Format) {
|
|
115
|
+
const sourceLabel = formatConfig.source === 'auto-detected' ? 'auto-detected'
|
|
116
|
+
: formatConfig.source === 'env-var' ? 'from .env'
|
|
117
|
+
: formatConfig.source === 'explicit-flag' ? '--format flag'
|
|
118
|
+
: 'default';
|
|
119
|
+
console.log(`Format: ${formatConfig.version} [${sourceLabel}]`);
|
|
120
|
+
}
|
|
121
|
+
// If format is newo_v2, always use SyncEngine (which will use V2ProjectSyncStrategy)
|
|
122
|
+
if (useSyncEngine || isV2Format) {
|
|
123
|
+
await pullWithSyncEngine(customerConfig, selectedCustomer, resourcesToFetch, verbose, silentOverwrite, formatConfig.version);
|
|
110
124
|
}
|
|
111
125
|
else {
|
|
112
|
-
// Legacy behavior: pull projects + attributes only
|
|
126
|
+
// Legacy behavior: pull projects + attributes only (cli_v1)
|
|
113
127
|
const accessToken = await getValidAccessToken(selectedCustomer);
|
|
114
128
|
const client = await makeClient(verbose, accessToken);
|
|
115
129
|
const projectId = selectedCustomer.projectId || null;
|
|
@@ -118,12 +132,22 @@ export async function handlePullCommand(customerConfig, args, verbose) {
|
|
|
118
132
|
}
|
|
119
133
|
else if (isMultiCustomer) {
|
|
120
134
|
if (verbose)
|
|
121
|
-
console.log(
|
|
122
|
-
console.log(
|
|
135
|
+
console.log(`No default customer specified, pulling from all ${allCustomers.length} customers`);
|
|
136
|
+
console.log(`Pulling from ${allCustomers.length} customers...`);
|
|
123
137
|
for (const customer of allCustomers) {
|
|
124
|
-
console.log(`\
|
|
125
|
-
|
|
126
|
-
|
|
138
|
+
console.log(`\nPulling from customer: ${customer.idn}`);
|
|
139
|
+
// Resolve format per customer (auto-detect from filesystem)
|
|
140
|
+
const formatConfig = resolveFormat(customer.idn, explicitFormat);
|
|
141
|
+
const isV2Format = formatConfig.version === 'newo_v2';
|
|
142
|
+
if (verbose || isV2Format) {
|
|
143
|
+
const sourceLabel = formatConfig.source === 'auto-detected' ? 'auto-detected'
|
|
144
|
+
: formatConfig.source === 'env-var' ? 'from .env'
|
|
145
|
+
: formatConfig.source === 'explicit-flag' ? '--format flag'
|
|
146
|
+
: 'default';
|
|
147
|
+
console.log(` Format: ${formatConfig.version} [${sourceLabel}]`);
|
|
148
|
+
}
|
|
149
|
+
if (useSyncEngine || isV2Format) {
|
|
150
|
+
await pullWithSyncEngine(customerConfig, customer, resourcesToFetch, verbose, silentOverwrite, formatConfig.version);
|
|
127
151
|
}
|
|
128
152
|
else {
|
|
129
153
|
const accessToken = await getValidAccessToken(customer);
|
|
@@ -132,7 +156,7 @@ export async function handlePullCommand(customerConfig, args, verbose) {
|
|
|
132
156
|
await pullAll(client, customer, projectId, verbose, silentOverwrite);
|
|
133
157
|
}
|
|
134
158
|
}
|
|
135
|
-
console.log(`\
|
|
159
|
+
console.log(`\nPull completed for all ${allCustomers.length} customers`);
|
|
136
160
|
}
|
|
137
161
|
}
|
|
138
162
|
//# sourceMappingURL=pull.js.map
|
|
@@ -15,6 +15,7 @@ import { getValidAccessToken } from '../../auth.js';
|
|
|
15
15
|
import { selectSingleCustomer, interactiveCustomerSelection } from '../customer-selection.js';
|
|
16
16
|
import { setupCli } from '../../cli-new/bootstrap.js';
|
|
17
17
|
import { PUSHABLE_RESOURCE_TYPES } from '../../cli-new/di/tokens.js';
|
|
18
|
+
import { resolveFormat } from '../../format/detect.js';
|
|
18
19
|
/**
|
|
19
20
|
* Parse resource list from comma-separated string
|
|
20
21
|
*/
|
|
@@ -44,10 +45,10 @@ function validateResources(resources) {
|
|
|
44
45
|
return { valid, invalid };
|
|
45
46
|
}
|
|
46
47
|
/**
|
|
47
|
-
* Push using
|
|
48
|
+
* Push using SyncEngine with selective sync and format support
|
|
48
49
|
*/
|
|
49
|
-
async function
|
|
50
|
-
const { syncEngine, logger } = setupCli(customerConfig, verbose);
|
|
50
|
+
async function pushWithSyncEngine(customerConfig, customer, resources, verbose, formatVersion) {
|
|
51
|
+
const { syncEngine, logger } = setupCli(customerConfig, verbose, formatVersion);
|
|
51
52
|
if (resources === 'all') {
|
|
52
53
|
const result = await syncEngine.pushAll(customer);
|
|
53
54
|
logger.info(`ā
Pushed: ${result.totalCreated} created, ${result.totalUpdated} updated, ${result.totalDeleted} deleted`);
|
|
@@ -103,47 +104,46 @@ export async function handlePushCommand(customerConfig, args, verbose) {
|
|
|
103
104
|
resourcesToPush = 'all';
|
|
104
105
|
console.log(`š¦ Pushing ALL resources`);
|
|
105
106
|
}
|
|
106
|
-
// Use
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
// Use SyncEngine if selective sync requested, otherwise use legacy for backward compatibility
|
|
108
|
+
const useSyncEngine = onlyResources.length > 0 || excludeResources.length > 0 || pushAllResources;
|
|
109
|
+
const explicitFormat = args.format;
|
|
110
|
+
/**
|
|
111
|
+
* Push for a single customer with format detection
|
|
112
|
+
*/
|
|
113
|
+
async function pushForCustomer(customer) {
|
|
114
|
+
const formatConfig = resolveFormat(customer.idn, explicitFormat);
|
|
115
|
+
const isV2Format = formatConfig.version === 'newo_v2';
|
|
116
|
+
if (verbose || isV2Format) {
|
|
117
|
+
const sourceLabel = formatConfig.source === 'auto-detected' ? 'auto-detected'
|
|
118
|
+
: formatConfig.source === 'env-var' ? 'from .env'
|
|
119
|
+
: formatConfig.source === 'explicit-flag' ? '--format flag'
|
|
120
|
+
: 'default';
|
|
121
|
+
console.log(`Format: ${formatConfig.version} [${sourceLabel}]`);
|
|
122
|
+
}
|
|
123
|
+
if (useSyncEngine || isV2Format) {
|
|
124
|
+
await pushWithSyncEngine(customerConfig, customer, resourcesToPush, verbose, formatConfig.version);
|
|
111
125
|
}
|
|
112
126
|
else {
|
|
113
|
-
|
|
114
|
-
const accessToken = await getValidAccessToken(selectedCustomer);
|
|
127
|
+
const accessToken = await getValidAccessToken(customer);
|
|
115
128
|
const client = await makeClient(verbose, accessToken);
|
|
116
|
-
await pushChanged(client,
|
|
129
|
+
await pushChanged(client, customer, verbose, shouldPublish);
|
|
117
130
|
}
|
|
118
131
|
}
|
|
132
|
+
if (selectedCustomer) {
|
|
133
|
+
await pushForCustomer(selectedCustomer);
|
|
134
|
+
}
|
|
119
135
|
else if (isMultiCustomer) {
|
|
120
|
-
// Multiple customers exist with no default, ask user
|
|
121
136
|
const customersToProcess = await interactiveCustomerSelection(allCustomers);
|
|
122
137
|
if (customersToProcess.length === 1) {
|
|
123
|
-
|
|
124
|
-
if (useV2Engine) {
|
|
125
|
-
await pushWithV2Engine(customerConfig, customer, resourcesToPush, verbose);
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
128
|
-
const accessToken = await getValidAccessToken(customer);
|
|
129
|
-
const client = await makeClient(verbose, accessToken);
|
|
130
|
-
await pushChanged(client, customer, verbose, shouldPublish);
|
|
131
|
-
}
|
|
138
|
+
await pushForCustomer(customersToProcess[0]);
|
|
132
139
|
}
|
|
133
140
|
else {
|
|
134
|
-
console.log(
|
|
141
|
+
console.log(`Pushing to ${customersToProcess.length} customers...`);
|
|
135
142
|
for (const customer of customersToProcess) {
|
|
136
|
-
console.log(`\
|
|
137
|
-
|
|
138
|
-
await pushWithV2Engine(customerConfig, customer, resourcesToPush, verbose);
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
const accessToken = await getValidAccessToken(customer);
|
|
142
|
-
const client = await makeClient(verbose, accessToken);
|
|
143
|
-
await pushChanged(client, customer, verbose, shouldPublish);
|
|
144
|
-
}
|
|
143
|
+
console.log(`\nPushing for customer: ${customer.idn}`);
|
|
144
|
+
await pushForCustomer(customer);
|
|
145
145
|
}
|
|
146
|
-
console.log(`\
|
|
146
|
+
console.log(`\nPush completed for all ${customersToProcess.length} customers`);
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
}
|
|
@@ -1,22 +1,61 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Status command handler
|
|
3
|
+
*
|
|
4
|
+
* Supports format-aware status with --format flag:
|
|
5
|
+
* newo status # auto-detect format per customer
|
|
6
|
+
* newo status --format newo_v2 # force V2 format
|
|
3
7
|
*/
|
|
4
8
|
import { status } from '../../sync.js';
|
|
5
9
|
import { selectSingleCustomer } from '../customer-selection.js';
|
|
10
|
+
import { setupCli } from '../../cli-new/bootstrap.js';
|
|
11
|
+
import { resolveFormat } from '../../format/detect.js';
|
|
12
|
+
async function statusForCustomer(customerConfig, customer, explicitFormat, verbose) {
|
|
13
|
+
const formatConfig = resolveFormat(customer.idn, explicitFormat);
|
|
14
|
+
const isV2Format = formatConfig.version === 'newo_v2';
|
|
15
|
+
if (verbose || isV2Format) {
|
|
16
|
+
const sourceLabel = formatConfig.source === 'auto-detected' ? 'auto-detected'
|
|
17
|
+
: formatConfig.source === 'env-var' ? 'from .env'
|
|
18
|
+
: formatConfig.source === 'explicit-flag' ? '--format flag'
|
|
19
|
+
: 'default';
|
|
20
|
+
console.log(`Format: ${formatConfig.version} [${sourceLabel}]`);
|
|
21
|
+
}
|
|
22
|
+
if (isV2Format) {
|
|
23
|
+
// Use SyncEngine with V2ProjectSyncStrategy
|
|
24
|
+
const { syncEngine, logger } = setupCli(customerConfig, verbose, formatConfig.version);
|
|
25
|
+
const report = await syncEngine.getStatus(customer);
|
|
26
|
+
logger.info(`\nStatus for customer: ${report.customer}`);
|
|
27
|
+
logger.info(`Total changes: ${report.totalChanges}\n`);
|
|
28
|
+
for (const resource of report.resources) {
|
|
29
|
+
if (resource.changedCount > 0) {
|
|
30
|
+
logger.info(`${resource.displayName}: ${resource.changedCount} change(s)`);
|
|
31
|
+
for (const change of resource.changes) {
|
|
32
|
+
const op = change.operation.toUpperCase().charAt(0);
|
|
33
|
+
logger.info(` ${op} ${change.path}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (report.totalChanges === 0) {
|
|
38
|
+
logger.info('No changes to push.');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// Legacy V1 status
|
|
43
|
+
await status(customer, verbose);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
6
46
|
export async function handleStatusCommand(customerConfig, args, verbose) {
|
|
7
47
|
const { selectedCustomer, allCustomers, isMultiCustomer } = selectSingleCustomer(customerConfig, args.customer);
|
|
48
|
+
const explicitFormat = args.format;
|
|
8
49
|
if (selectedCustomer) {
|
|
9
|
-
|
|
10
|
-
await status(selectedCustomer, verbose);
|
|
50
|
+
await statusForCustomer(customerConfig, selectedCustomer, explicitFormat, verbose);
|
|
11
51
|
}
|
|
12
52
|
else if (isMultiCustomer) {
|
|
13
|
-
|
|
14
|
-
console.log(`š Checking status for ${allCustomers.length} customers...`);
|
|
53
|
+
console.log(`Checking status for ${allCustomers.length} customers...`);
|
|
15
54
|
for (const customer of allCustomers) {
|
|
16
|
-
console.log(`\
|
|
17
|
-
await
|
|
55
|
+
console.log(`\nStatus for customer: ${customer.idn}`);
|
|
56
|
+
await statusForCustomer(customerConfig, customer, explicitFormat, verbose);
|
|
18
57
|
}
|
|
19
|
-
console.log(`\
|
|
58
|
+
console.log(`\nStatus check completed for all ${allCustomers.length} customers`);
|
|
20
59
|
}
|
|
21
60
|
}
|
|
22
61
|
//# sourceMappingURL=status.js.map
|