newo 3.4.2 → 3.6.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/.env.example +5 -0
- package/CHANGELOG.md +21 -0
- 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/dist/cli.js
CHANGED
|
@@ -46,6 +46,7 @@ import { handleAddProjectCommand } from './cli/commands/add-project.js';
|
|
|
46
46
|
import { handleWatchCommand } from './cli/commands/watch.js';
|
|
47
47
|
import { handleDiffCommand } from './cli/commands/diff.js';
|
|
48
48
|
import { handleLogsCommand } from './cli/commands/logs.js';
|
|
49
|
+
import { handleExportCommand } from './cli/commands/export.js';
|
|
49
50
|
dotenv.config();
|
|
50
51
|
async function main() {
|
|
51
52
|
try {
|
|
@@ -100,6 +101,9 @@ async function main() {
|
|
|
100
101
|
case 'logs':
|
|
101
102
|
await handleLogsCommand(customerConfig, args, verbose);
|
|
102
103
|
break;
|
|
104
|
+
case 'export':
|
|
105
|
+
await handleExportCommand(customerConfig, args, verbose);
|
|
106
|
+
break;
|
|
103
107
|
case 'conversations':
|
|
104
108
|
await handleConversationsCommand(customerConfig, args, verbose);
|
|
105
109
|
break;
|
|
@@ -79,6 +79,11 @@ export declare class ProjectSyncStrategy implements ISyncStrategy<ProjectMeta, L
|
|
|
79
79
|
* Push a skill update
|
|
80
80
|
*/
|
|
81
81
|
private pushSkillUpdate;
|
|
82
|
+
/**
|
|
83
|
+
* Push a library skill update
|
|
84
|
+
* Path: newo_customers/{customer}/projects/{project}/libraries/{lib}/{skill}/{skill}.jinja
|
|
85
|
+
*/
|
|
86
|
+
private pushLibrarySkillUpdate;
|
|
82
87
|
/**
|
|
83
88
|
* Push a new entity
|
|
84
89
|
*/
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import fs from 'fs-extra';
|
|
14
14
|
import yaml from 'js-yaml';
|
|
15
|
-
import { listProjects, listAgents, listFlowSkills, listFlowEvents, listFlowStates, updateSkill, publishFlow } from '../../../api.js';
|
|
16
|
-
import { ensureState, writeFileSafe, mapPath, projectMetadataPath, agentMetadataPath, flowMetadataPath, skillMetadataPath, skillScriptPath, skillFolderPath, flowsYamlPath, customerProjectsDir, projectDir } from '../../../fsutil.js';
|
|
15
|
+
import { listProjects, listAgents, listFlowSkills, listFlowEvents, listFlowStates, updateSkill, publishFlow, listLibraries, updateLibrarySkill, } from '../../../api.js';
|
|
16
|
+
import { ensureState, writeFileSafe, mapPath, projectMetadataPath, agentMetadataPath, flowMetadataPath, skillMetadataPath, skillScriptPath, skillFolderPath, flowsYamlPath, customerProjectsDir, projectDir, libraryMetadataPath, librarySkillMetadataPath, librarySkillScriptPath, } from '../../../fsutil.js';
|
|
17
17
|
import { sha256, saveHashes, loadHashes } from '../../../hash.js';
|
|
18
18
|
import { generateFlowsYaml } from '../../../sync/metadata.js';
|
|
19
19
|
import { findSkillScriptFiles, isContentDifferent, getExtensionForRunner, getSingleSkillFile, validateSkillFolder } from '../../../sync/skill-files.js';
|
|
@@ -129,6 +129,47 @@ export class ProjectSyncStrategy {
|
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
|
+
// Pull libraries for this project
|
|
133
|
+
try {
|
|
134
|
+
const libraries = await listLibraries(client, project.id);
|
|
135
|
+
if (libraries.length > 0) {
|
|
136
|
+
this.logger.verbose(` Found ${libraries.length} libraries in project ${project.idn}`);
|
|
137
|
+
projectData.libraries = {};
|
|
138
|
+
for (const lib of libraries) {
|
|
139
|
+
projectData.libraries[lib.idn] = {
|
|
140
|
+
id: lib.id,
|
|
141
|
+
skills: {}
|
|
142
|
+
};
|
|
143
|
+
// Save library metadata
|
|
144
|
+
const libMetaPath = libraryMetadataPath(customer.idn, project.idn, lib.idn);
|
|
145
|
+
const libMeta = { id: lib.id, idn: lib.idn };
|
|
146
|
+
const libMetaYaml = yaml.dump(libMeta, { indent: 2, quotingType: '"', forceQuotes: false });
|
|
147
|
+
await writeFileSafe(libMetaPath, libMetaYaml);
|
|
148
|
+
hashes[libMetaPath] = sha256(libMetaYaml);
|
|
149
|
+
for (const skill of lib.skills) {
|
|
150
|
+
// Save skill metadata
|
|
151
|
+
const skillMetaPath = librarySkillMetadataPath(customer.idn, project.idn, lib.idn, skill.idn);
|
|
152
|
+
const skillMeta = {
|
|
153
|
+
id: skill.id, idn: skill.idn, title: skill.title,
|
|
154
|
+
runner_type: skill.runner_type, model: skill.model,
|
|
155
|
+
parameters: [...skill.parameters], path: skill.path
|
|
156
|
+
};
|
|
157
|
+
const skillMetaYaml = yaml.dump(skillMeta, { indent: 2, quotingType: '"', forceQuotes: false });
|
|
158
|
+
await writeFileSafe(skillMetaPath, skillMetaYaml);
|
|
159
|
+
hashes[skillMetaPath] = sha256(skillMetaYaml);
|
|
160
|
+
// Save skill script
|
|
161
|
+
const scriptContent = skill.prompt_script || '';
|
|
162
|
+
const scriptPath = librarySkillScriptPath(customer.idn, project.idn, lib.idn, skill.idn, skill.runner_type);
|
|
163
|
+
await writeFileSafe(scriptPath, scriptContent);
|
|
164
|
+
hashes[scriptPath] = sha256(scriptContent);
|
|
165
|
+
projectData.libraries[lib.idn].skills[skill.idn] = skillMeta;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
this.logger.verbose(` Could not pull libraries for project ${project.idn}: ${error instanceof Error ? error.message : String(error)}`);
|
|
172
|
+
}
|
|
132
173
|
existingMap.projects[project.idn] = projectData;
|
|
133
174
|
projects.push(localProject);
|
|
134
175
|
}
|
|
@@ -313,12 +354,17 @@ export class ProjectSyncStrategy {
|
|
|
313
354
|
for (const change of changes) {
|
|
314
355
|
try {
|
|
315
356
|
if (change.operation === 'modified') {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
357
|
+
const isLibrary = change.path.includes('/libraries/');
|
|
358
|
+
if (isLibrary) {
|
|
359
|
+
const updateResult = await this.pushLibrarySkillUpdate(client, change, mapData, newHashes);
|
|
360
|
+
result.updated += updateResult;
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
const updateResult = await this.pushSkillUpdate(client, customer, change, mapData, newHashes);
|
|
364
|
+
result.updated += updateResult;
|
|
365
|
+
}
|
|
319
366
|
}
|
|
320
367
|
else if (change.operation === 'created') {
|
|
321
|
-
// Create new entity
|
|
322
368
|
const createResult = await this.pushNewEntity(client, customer, change, mapData, newHashes);
|
|
323
369
|
result.created += createResult;
|
|
324
370
|
}
|
|
@@ -372,6 +418,29 @@ export class ProjectSyncStrategy {
|
|
|
372
418
|
this.logger.info(`↑ Pushed: ${skillIdn}`);
|
|
373
419
|
return 1;
|
|
374
420
|
}
|
|
421
|
+
/**
|
|
422
|
+
* Push a library skill update
|
|
423
|
+
* Path: newo_customers/{customer}/projects/{project}/libraries/{lib}/{skill}/{skill}.jinja
|
|
424
|
+
*/
|
|
425
|
+
async pushLibrarySkillUpdate(client, change, mapData, newHashes) {
|
|
426
|
+
const pathParts = change.path.split('/');
|
|
427
|
+
const skillIdn = pathParts[pathParts.length - 2] || '';
|
|
428
|
+
const libIdn = pathParts[pathParts.length - 3] || '';
|
|
429
|
+
const projectIdn = pathParts[pathParts.length - 5] || '';
|
|
430
|
+
const projectData = mapData.projects[projectIdn];
|
|
431
|
+
const libData = projectData?.libraries?.[libIdn];
|
|
432
|
+
const skillData = libData?.skills[skillIdn];
|
|
433
|
+
if (!skillData || !libData) {
|
|
434
|
+
throw new Error(`Library skill ${skillIdn} not found in project map`);
|
|
435
|
+
}
|
|
436
|
+
const content = await fs.readFile(change.path, 'utf8');
|
|
437
|
+
await updateLibrarySkill(client, libData.id, skillData.id, {
|
|
438
|
+
prompt_script: content,
|
|
439
|
+
});
|
|
440
|
+
newHashes[change.path] = sha256(content);
|
|
441
|
+
this.logger.info(`Pushed library skill: ${libIdn}/${skillIdn}`);
|
|
442
|
+
return 1;
|
|
443
|
+
}
|
|
375
444
|
/**
|
|
376
445
|
* Push a new entity
|
|
377
446
|
*/
|
|
@@ -415,7 +484,7 @@ export class ProjectSyncStrategy {
|
|
|
415
484
|
}
|
|
416
485
|
const hashes = await loadHashes(customer.idn);
|
|
417
486
|
const mapData = await fs.readJson(mapFile);
|
|
418
|
-
// Scan for changed skill scripts
|
|
487
|
+
// Scan for changed flow skill scripts
|
|
419
488
|
for (const [projectIdn, projectData] of Object.entries(mapData.projects)) {
|
|
420
489
|
for (const [agentIdn, agentData] of Object.entries(projectData.agents)) {
|
|
421
490
|
for (const [flowIdn, flowData] of Object.entries(agentData.flows)) {
|
|
@@ -426,7 +495,7 @@ export class ProjectSyncStrategy {
|
|
|
426
495
|
const storedHash = hashes[skillFile.filePath];
|
|
427
496
|
if (storedHash !== currentHash) {
|
|
428
497
|
changes.push({
|
|
429
|
-
item: {},
|
|
498
|
+
item: {},
|
|
430
499
|
operation: 'modified',
|
|
431
500
|
path: skillFile.filePath
|
|
432
501
|
});
|
|
@@ -435,6 +504,26 @@ export class ProjectSyncStrategy {
|
|
|
435
504
|
}
|
|
436
505
|
}
|
|
437
506
|
}
|
|
507
|
+
// Scan for changed library skill scripts
|
|
508
|
+
if (projectData.libraries) {
|
|
509
|
+
for (const [libIdn, libData] of Object.entries(projectData.libraries)) {
|
|
510
|
+
for (const [skillIdn, skillMeta] of Object.entries(libData.skills)) {
|
|
511
|
+
const scriptPath = librarySkillScriptPath(customer.idn, projectIdn, libIdn, skillIdn, skillMeta.runner_type);
|
|
512
|
+
if (await fs.pathExists(scriptPath)) {
|
|
513
|
+
const content = await fs.readFile(scriptPath, 'utf8');
|
|
514
|
+
const currentHash = sha256(content);
|
|
515
|
+
const storedHash = hashes[scriptPath];
|
|
516
|
+
if (storedHash !== currentHash) {
|
|
517
|
+
changes.push({
|
|
518
|
+
item: {},
|
|
519
|
+
operation: 'modified',
|
|
520
|
+
path: scriptPath
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
438
527
|
}
|
|
439
528
|
return changes;
|
|
440
529
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* V2ProjectSyncStrategy - Handles synchronization in newo_v2 format
|
|
3
|
+
*
|
|
4
|
+
* Uses the SAME V1 API endpoints as ProjectSyncStrategy but writes/reads
|
|
5
|
+
* files in the newo_v2 directory layout:
|
|
6
|
+
* {CustomerIdn}/
|
|
7
|
+
* import_version.txt
|
|
8
|
+
* {ProjectIdn}/
|
|
9
|
+
* {project_idn}.yaml
|
|
10
|
+
* agents/{AgentIdn}/
|
|
11
|
+
* agent.yaml
|
|
12
|
+
* flows/{FlowIdn}/
|
|
13
|
+
* {FlowIdn}.yaml (inline skill defs, events, state_fields)
|
|
14
|
+
* skills/{SkillIdn}.nsl|.nslg
|
|
15
|
+
*/
|
|
16
|
+
import type { ISyncStrategy, PullOptions, PullResult, PushResult, ChangeItem, ValidationResult, StatusSummary } from './ISyncStrategy.js';
|
|
17
|
+
import type { CustomerConfig, ILogger } from '../../resources/common/types.js';
|
|
18
|
+
import type { ProjectMeta } from '../../../types.js';
|
|
19
|
+
import type { LocalProjectData, ApiClientFactory } from './ProjectSyncStrategy.js';
|
|
20
|
+
/**
|
|
21
|
+
* V2ProjectSyncStrategy - same API, newo_v2 file layout
|
|
22
|
+
*/
|
|
23
|
+
export declare class V2ProjectSyncStrategy implements ISyncStrategy<ProjectMeta, LocalProjectData> {
|
|
24
|
+
private apiClientFactory;
|
|
25
|
+
private logger;
|
|
26
|
+
readonly resourceType = "projects";
|
|
27
|
+
readonly displayName = "Projects (newo_v2)";
|
|
28
|
+
constructor(apiClientFactory: ApiClientFactory, logger: ILogger);
|
|
29
|
+
pull(customer: CustomerConfig, options?: PullOptions): Promise<PullResult<LocalProjectData>>;
|
|
30
|
+
/**
|
|
31
|
+
* Pull a single agent in V2 format
|
|
32
|
+
*/
|
|
33
|
+
private pullAgent;
|
|
34
|
+
/**
|
|
35
|
+
* Pull a single flow in V2 format
|
|
36
|
+
*
|
|
37
|
+
* In V2, the flow YAML contains inline skill definitions, events, and state_fields.
|
|
38
|
+
* Skills are written to flows/{FlowIdn}/skills/{SkillIdn}.nsl|.nslg
|
|
39
|
+
*/
|
|
40
|
+
private pullFlow;
|
|
41
|
+
/**
|
|
42
|
+
* Pull a single skill script in V2 format
|
|
43
|
+
*
|
|
44
|
+
* Script goes to: flows/{FlowIdn}/skills/{SkillIdn}.nsl|.nslg
|
|
45
|
+
* No separate metadata.yaml - metadata is inline in the flow YAML
|
|
46
|
+
*/
|
|
47
|
+
private pullSkill;
|
|
48
|
+
/**
|
|
49
|
+
* Pull a library and its skills in V2 format
|
|
50
|
+
*
|
|
51
|
+
* Writes:
|
|
52
|
+
* {project}/libraries/{lib}/{lib}.yaml (with inline skill list)
|
|
53
|
+
* {project}/libraries/{lib}/skills/{skill}.nsl|.nslg
|
|
54
|
+
*/
|
|
55
|
+
private pullLibrary;
|
|
56
|
+
push(customer: CustomerConfig, changes?: ChangeItem<LocalProjectData>[]): Promise<PushResult>;
|
|
57
|
+
/**
|
|
58
|
+
* Push a V2 skill update
|
|
59
|
+
*
|
|
60
|
+
* V2 path: newo_customers/{cust}/{proj}/agents/{agent}/flows/{flow}/skills/{skill}.nsl
|
|
61
|
+
*/
|
|
62
|
+
private pushV2SkillUpdate;
|
|
63
|
+
/**
|
|
64
|
+
* Push a V2 library skill update
|
|
65
|
+
* Path: .../newo_customers/{cust}/{proj}/libraries/{lib}/skills/{skillFile}
|
|
66
|
+
*/
|
|
67
|
+
private pushV2LibrarySkillUpdate;
|
|
68
|
+
/**
|
|
69
|
+
* Publish all flows
|
|
70
|
+
*/
|
|
71
|
+
private publishAllFlows;
|
|
72
|
+
getChanges(customer: CustomerConfig): Promise<ChangeItem<LocalProjectData>[]>;
|
|
73
|
+
validate(customer: CustomerConfig, _items: LocalProjectData[]): Promise<ValidationResult>;
|
|
74
|
+
getStatus(customer: CustomerConfig): Promise<StatusSummary>;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Factory function for creating V2ProjectSyncStrategy
|
|
78
|
+
*/
|
|
79
|
+
export declare function createV2ProjectSyncStrategy(apiClientFactory: ApiClientFactory, logger: ILogger): V2ProjectSyncStrategy;
|
|
80
|
+
//# sourceMappingURL=V2ProjectSyncStrategy.d.ts.map
|