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.
Files changed (64) hide show
  1. package/.env.example +5 -0
  2. package/CHANGELOG.md +21 -0
  3. package/dist/api.d.ts +18 -0
  4. package/dist/api.js +28 -0
  5. package/dist/cli/commands/export.d.ts +3 -0
  6. package/dist/cli/commands/export.js +62 -0
  7. package/dist/cli/commands/help.js +54 -42
  8. package/dist/cli/commands/pull.js +38 -14
  9. package/dist/cli/commands/push.js +32 -32
  10. package/dist/cli/commands/status.js +46 -7
  11. package/dist/cli-new/bootstrap.d.ts +7 -1
  12. package/dist/cli-new/bootstrap.js +11 -5
  13. package/dist/cli-new/di/tokens.d.ts +1 -0
  14. package/dist/cli-new/di/tokens.js +1 -0
  15. package/dist/cli.js +4 -0
  16. package/dist/domain/strategies/sync/ProjectSyncStrategy.d.ts +5 -0
  17. package/dist/domain/strategies/sync/ProjectSyncStrategy.js +97 -8
  18. package/dist/domain/strategies/sync/V2ProjectSyncStrategy.d.ts +80 -0
  19. package/dist/domain/strategies/sync/V2ProjectSyncStrategy.js +725 -0
  20. package/dist/env.d.ts +1 -0
  21. package/dist/env.js +1 -0
  22. package/dist/format/detect.d.ts +14 -0
  23. package/dist/format/detect.js +105 -0
  24. package/dist/format/extensions.d.ts +26 -0
  25. package/dist/format/extensions.js +45 -0
  26. package/dist/format/index.d.ts +11 -0
  27. package/dist/format/index.js +11 -0
  28. package/dist/format/paths-v2.d.ts +31 -0
  29. package/dist/format/paths-v2.js +104 -0
  30. package/dist/format/types.d.ts +28 -0
  31. package/dist/format/types.js +21 -0
  32. package/dist/format/v2-yaml.d.ts +143 -0
  33. package/dist/format/v2-yaml.js +222 -0
  34. package/dist/format/yaml-patch.d.ts +14 -0
  35. package/dist/format/yaml-patch.js +184 -0
  36. package/dist/fsutil.d.ts +10 -0
  37. package/dist/fsutil.js +25 -0
  38. package/dist/sync/attributes.js +3 -3
  39. package/dist/sync/skill-files.js +2 -2
  40. package/dist/types.d.ts +5 -0
  41. package/package.json +1 -1
  42. package/src/api.ts +64 -0
  43. package/src/cli/commands/export.ts +78 -0
  44. package/src/cli/commands/help.ts +54 -42
  45. package/src/cli/commands/pull.ts +46 -15
  46. package/src/cli/commands/push.ts +38 -31
  47. package/src/cli/commands/status.ts +59 -9
  48. package/src/cli-new/bootstrap.ts +19 -7
  49. package/src/cli-new/di/tokens.ts +1 -0
  50. package/src/cli.ts +5 -0
  51. package/src/domain/strategies/sync/ProjectSyncStrategy.ts +122 -8
  52. package/src/domain/strategies/sync/V2ProjectSyncStrategy.ts +1007 -0
  53. package/src/env.ts +2 -0
  54. package/src/format/detect.ts +123 -0
  55. package/src/format/extensions.ts +61 -0
  56. package/src/format/index.ts +66 -0
  57. package/src/format/paths-v2.ts +207 -0
  58. package/src/format/types.ts +40 -0
  59. package/src/format/v2-yaml.ts +345 -0
  60. package/src/format/yaml-patch.ts +208 -0
  61. package/src/fsutil.ts +37 -0
  62. package/src/sync/attributes.ts +3 -3
  63. package/src/sync/skill-files.ts +2 -2
  64. package/src/types.ts +6 -0
@@ -41,6 +41,7 @@ export declare const TOKENS: {
41
41
  readonly CUSTOMER_SELECTOR: symbol;
42
42
  readonly CUSTOMER_CONFIG: symbol;
43
43
  readonly ENVIRONMENT: symbol;
44
+ readonly FORMAT_VERSION: symbol;
44
45
  };
45
46
  /**
46
47
  * Type for the tokens object
@@ -53,6 +53,7 @@ export const TOKENS = {
53
53
  // Configuration
54
54
  CUSTOMER_CONFIG: Symbol('CustomerConfig'),
55
55
  ENVIRONMENT: Symbol('Environment'),
56
+ FORMAT_VERSION: Symbol('FormatVersion'),
56
57
  };
57
58
  /**
58
59
  * Resource types for selective sync
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
- // Update existing skill
317
- const updateResult = await this.pushSkillUpdate(client, customer, change, mapData, newHashes);
318
- result.updated += updateResult;
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: {}, // Simplified for now
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