newo 3.4.1 → 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 (69) hide show
  1. package/.env.example +5 -0
  2. package/CHANGELOG.md +31 -0
  3. package/dist/api.d.ts +18 -0
  4. package/dist/api.js +28 -0
  5. package/dist/cli/commands/create-attribute.js +1 -1
  6. package/dist/cli/commands/export.d.ts +3 -0
  7. package/dist/cli/commands/export.js +62 -0
  8. package/dist/cli/commands/help.js +54 -42
  9. package/dist/cli/commands/pull.js +38 -14
  10. package/dist/cli/commands/push.js +32 -32
  11. package/dist/cli/commands/status.js +46 -7
  12. package/dist/cli/commands/update-attribute.d.ts +3 -0
  13. package/dist/cli/commands/update-attribute.js +78 -0
  14. package/dist/cli-new/bootstrap.d.ts +7 -1
  15. package/dist/cli-new/bootstrap.js +11 -5
  16. package/dist/cli-new/di/tokens.d.ts +1 -0
  17. package/dist/cli-new/di/tokens.js +1 -0
  18. package/dist/cli.js +8 -0
  19. package/dist/domain/strategies/sync/ProjectSyncStrategy.d.ts +5 -0
  20. package/dist/domain/strategies/sync/ProjectSyncStrategy.js +97 -8
  21. package/dist/domain/strategies/sync/V2ProjectSyncStrategy.d.ts +80 -0
  22. package/dist/domain/strategies/sync/V2ProjectSyncStrategy.js +725 -0
  23. package/dist/env.d.ts +1 -0
  24. package/dist/env.js +1 -0
  25. package/dist/format/detect.d.ts +14 -0
  26. package/dist/format/detect.js +105 -0
  27. package/dist/format/extensions.d.ts +26 -0
  28. package/dist/format/extensions.js +45 -0
  29. package/dist/format/index.d.ts +11 -0
  30. package/dist/format/index.js +11 -0
  31. package/dist/format/paths-v2.d.ts +31 -0
  32. package/dist/format/paths-v2.js +104 -0
  33. package/dist/format/types.d.ts +28 -0
  34. package/dist/format/types.js +21 -0
  35. package/dist/format/v2-yaml.d.ts +143 -0
  36. package/dist/format/v2-yaml.js +222 -0
  37. package/dist/format/yaml-patch.d.ts +14 -0
  38. package/dist/format/yaml-patch.js +184 -0
  39. package/dist/fsutil.d.ts +10 -0
  40. package/dist/fsutil.js +25 -0
  41. package/dist/sync/attributes.js +3 -3
  42. package/dist/sync/skill-files.js +2 -2
  43. package/dist/types.d.ts +5 -0
  44. package/package.json +1 -1
  45. package/src/api.ts +64 -0
  46. package/src/cli/commands/create-attribute.ts +1 -1
  47. package/src/cli/commands/export.ts +78 -0
  48. package/src/cli/commands/help.ts +54 -42
  49. package/src/cli/commands/pull.ts +46 -15
  50. package/src/cli/commands/push.ts +38 -31
  51. package/src/cli/commands/status.ts +59 -9
  52. package/src/cli/commands/update-attribute.ts +82 -0
  53. package/src/cli-new/bootstrap.ts +19 -7
  54. package/src/cli-new/di/tokens.ts +1 -0
  55. package/src/cli.ts +10 -0
  56. package/src/domain/strategies/sync/ProjectSyncStrategy.ts +122 -8
  57. package/src/domain/strategies/sync/V2ProjectSyncStrategy.ts +1007 -0
  58. package/src/env.ts +2 -0
  59. package/src/format/detect.ts +123 -0
  60. package/src/format/extensions.ts +61 -0
  61. package/src/format/index.ts +66 -0
  62. package/src/format/paths-v2.ts +207 -0
  63. package/src/format/types.ts +40 -0
  64. package/src/format/v2-yaml.ts +345 -0
  65. package/src/format/yaml-patch.ts +208 -0
  66. package/src/fsutil.ts +37 -0
  67. package/src/sync/attributes.ts +3 -3
  68. package/src/sync/skill-files.ts +2 -2
  69. package/src/types.ts +6 -0
@@ -11,6 +11,8 @@ import { ConsoleLogger, type ILogger, type CustomerConfig, type MultiCustomerCon
11
11
  import { SyncEngine, type SyncEngineOptions } from '../application/sync/SyncEngine.js';
12
12
  import { MigrationEngine, TransformService } from '../application/migration/MigrationEngine.js';
13
13
  import { ProjectSyncStrategy, createProjectSyncStrategy } from '../domain/strategies/sync/ProjectSyncStrategy.js';
14
+ import { createV2ProjectSyncStrategy } from '../domain/strategies/sync/V2ProjectSyncStrategy.js';
15
+ import type { FormatVersion } from '../format/types.js';
14
16
  import { AttributeSyncStrategy, createAttributeSyncStrategy } from '../domain/strategies/sync/AttributeSyncStrategy.js';
15
17
  import { IntegrationSyncStrategy, createIntegrationSyncStrategy } from '../domain/strategies/sync/IntegrationSyncStrategy.js';
16
18
  import { AkbSyncStrategy, createAkbSyncStrategy } from '../domain/strategies/sync/AkbSyncStrategy.js';
@@ -30,7 +32,7 @@ export async function createApiClient(customer: CustomerConfig, verbose: boolean
30
32
  process.env.NEWO_PROJECT_ID = customer.projectId;
31
33
  }
32
34
 
33
- const token = await getValidAccessToken();
35
+ const token = await getValidAccessToken(customer);
34
36
  return makeClient(verbose, token);
35
37
  }
36
38
 
@@ -47,6 +49,12 @@ export interface BootstrapOptions {
47
49
  * Sync engine options
48
50
  */
49
51
  syncEngineOptions?: SyncEngineOptions;
52
+
53
+ /**
54
+ * Format version for project sync strategy
55
+ * 'newo_v2' uses V2ProjectSyncStrategy, 'cli_v1' (default) uses ProjectSyncStrategy
56
+ */
57
+ formatVersion?: FormatVersion | undefined;
50
58
  }
51
59
 
52
60
  /**
@@ -73,10 +81,13 @@ export function createServiceContainer(
73
81
 
74
82
  // === Domain Layer - Sync Strategies ===
75
83
 
76
- // Project Sync Strategy
77
- container.registerSingleton(TOKENS.PROJECT_SYNC_STRATEGY, () =>
78
- createProjectSyncStrategy(createApiClient, container.get<ILogger>(TOKENS.LOGGER))
79
- );
84
+ // Project Sync Strategy (format-conditional: cli_v1 or newo_v2)
85
+ container.registerSingleton(TOKENS.PROJECT_SYNC_STRATEGY, () => {
86
+ if (options.formatVersion === 'newo_v2') {
87
+ return createV2ProjectSyncStrategy(createApiClient, container.get<ILogger>(TOKENS.LOGGER));
88
+ }
89
+ return createProjectSyncStrategy(createApiClient, container.get<ILogger>(TOKENS.LOGGER));
90
+ });
80
91
 
81
92
  // Attribute Sync Strategy
82
93
  container.registerSingleton(TOKENS.ATTRIBUTE_SYNC_STRATEGY, () =>
@@ -151,14 +162,15 @@ export function getLogger(container: ServiceContainer): ILogger {
151
162
  */
152
163
  export function setupCli(
153
164
  customerConfig: MultiCustomerConfig,
154
- verbose: boolean = false
165
+ verbose: boolean = false,
166
+ formatVersion?: FormatVersion
155
167
  ): {
156
168
  container: ServiceContainer;
157
169
  syncEngine: SyncEngine;
158
170
  migrationEngine: MigrationEngine;
159
171
  logger: ILogger;
160
172
  } {
161
- const container = createServiceContainer(customerConfig, { verbose });
173
+ const container = createServiceContainer(customerConfig, { verbose, formatVersion });
162
174
 
163
175
  return {
164
176
  container,
@@ -64,6 +64,7 @@ export const TOKENS = {
64
64
  // Configuration
65
65
  CUSTOMER_CONFIG: Symbol('CustomerConfig'),
66
66
  ENVIRONMENT: Symbol('Environment'),
67
+ FORMAT_VERSION: Symbol('FormatVersion'),
67
68
  } as const;
68
69
 
69
70
  /**
package/src/cli.ts CHANGED
@@ -29,6 +29,7 @@ import { handleCreateStateCommand } from './cli/commands/create-state.js';
29
29
  import { handleCreateParameterCommand } from './cli/commands/create-parameter.js';
30
30
  import { handleCreatePersonaCommand } from './cli/commands/create-persona.js';
31
31
  import { handleCreateAttributeCommand } from './cli/commands/create-attribute.js';
32
+ import { handleUpdateAttributeCommand } from './cli/commands/update-attribute.js';
32
33
  import { handleSandboxCommand } from './cli/commands/sandbox.js';
33
34
  import { handlePullIntegrationsCommand } from './cli/commands/pull-integrations.js';
34
35
  import { handlePushIntegrationsCommand } from './cli/commands/push-integrations.js';
@@ -45,6 +46,7 @@ import { handleAddProjectCommand } from './cli/commands/add-project.js';
45
46
  import { handleWatchCommand } from './cli/commands/watch.js';
46
47
  import { handleDiffCommand } from './cli/commands/diff.js';
47
48
  import { handleLogsCommand } from './cli/commands/logs.js';
49
+ import { handleExportCommand } from './cli/commands/export.js';
48
50
  import type { CliArgs, NewoApiError } from './types.js';
49
51
 
50
52
  dotenv.config();
@@ -110,6 +112,10 @@ async function main(): Promise<void> {
110
112
  await handleLogsCommand(customerConfig, args, verbose);
111
113
  break;
112
114
 
115
+ case 'export':
116
+ await handleExportCommand(customerConfig, args, verbose);
117
+ break;
118
+
113
119
  case 'conversations':
114
120
  await handleConversationsCommand(customerConfig, args, verbose);
115
121
  break;
@@ -182,6 +188,10 @@ async function main(): Promise<void> {
182
188
  await handleCreateAttributeCommand(customerConfig, args, verbose);
183
189
  break;
184
190
 
191
+ case 'update-attribute':
192
+ await handleUpdateAttributeCommand(customerConfig, args, verbose);
193
+ break;
194
+
185
195
  case 'pull-integrations':
186
196
  await handlePullIntegrationsCommand(customerConfig, args, verbose);
187
197
  break;
@@ -46,7 +46,9 @@ import {
46
46
  listFlowEvents,
47
47
  listFlowStates,
48
48
  updateSkill,
49
- publishFlow
49
+ publishFlow,
50
+ listLibraries,
51
+ updateLibrarySkill,
50
52
  } from '../../../api.js';
51
53
  import {
52
54
  ensureState,
@@ -60,7 +62,10 @@ import {
60
62
  skillFolderPath,
61
63
  flowsYamlPath,
62
64
  customerProjectsDir,
63
- projectDir
65
+ projectDir,
66
+ libraryMetadataPath,
67
+ librarySkillMetadataPath,
68
+ librarySkillScriptPath,
64
69
  } from '../../../fsutil.js';
65
70
  import { sha256, saveHashes, loadHashes } from '../../../hash.js';
66
71
  import { generateFlowsYaml } from '../../../sync/metadata.js';
@@ -241,6 +246,52 @@ export class ProjectSyncStrategy implements ISyncStrategy<ProjectMeta, LocalProj
241
246
  }
242
247
  }
243
248
 
249
+ // Pull libraries for this project
250
+ try {
251
+ const libraries = await listLibraries(client, project.id);
252
+ if (libraries.length > 0) {
253
+ this.logger.verbose(` Found ${libraries.length} libraries in project ${project.idn}`);
254
+ projectData.libraries = {};
255
+
256
+ for (const lib of libraries) {
257
+ projectData.libraries[lib.idn] = {
258
+ id: lib.id,
259
+ skills: {}
260
+ };
261
+
262
+ // Save library metadata
263
+ const libMetaPath = libraryMetadataPath(customer.idn, project.idn, lib.idn);
264
+ const libMeta = { id: lib.id, idn: lib.idn };
265
+ const libMetaYaml = yaml.dump(libMeta, { indent: 2, quotingType: '"', forceQuotes: false });
266
+ await writeFileSafe(libMetaPath, libMetaYaml);
267
+ hashes[libMetaPath] = sha256(libMetaYaml);
268
+
269
+ for (const skill of lib.skills) {
270
+ // Save skill metadata
271
+ const skillMetaPath = librarySkillMetadataPath(customer.idn, project.idn, lib.idn, skill.idn);
272
+ const skillMeta: SkillMetadata = {
273
+ id: skill.id, idn: skill.idn, title: skill.title,
274
+ runner_type: skill.runner_type, model: skill.model,
275
+ parameters: [...skill.parameters], path: skill.path
276
+ };
277
+ const skillMetaYaml = yaml.dump(skillMeta, { indent: 2, quotingType: '"', forceQuotes: false });
278
+ await writeFileSafe(skillMetaPath, skillMetaYaml);
279
+ hashes[skillMetaPath] = sha256(skillMetaYaml);
280
+
281
+ // Save skill script
282
+ const scriptContent = skill.prompt_script || '';
283
+ const scriptPath = librarySkillScriptPath(customer.idn, project.idn, lib.idn, skill.idn, skill.runner_type);
284
+ await writeFileSafe(scriptPath, scriptContent);
285
+ hashes[scriptPath] = sha256(scriptContent);
286
+
287
+ projectData.libraries[lib.idn]!.skills[skill.idn] = skillMeta;
288
+ }
289
+ }
290
+ }
291
+ } catch (error) {
292
+ this.logger.verbose(` Could not pull libraries for project ${project.idn}: ${error instanceof Error ? error.message : String(error)}`);
293
+ }
294
+
244
295
  existingMap.projects[project.idn] = projectData;
245
296
  projects.push(localProject);
246
297
  }
@@ -489,11 +540,15 @@ export class ProjectSyncStrategy implements ISyncStrategy<ProjectMeta, LocalProj
489
540
  for (const change of changes) {
490
541
  try {
491
542
  if (change.operation === 'modified') {
492
- // Update existing skill
493
- const updateResult = await this.pushSkillUpdate(client, customer, change, mapData, newHashes);
494
- result.updated += updateResult;
543
+ const isLibrary = change.path.includes('/libraries/');
544
+ if (isLibrary) {
545
+ const updateResult = await this.pushLibrarySkillUpdate(client, change, mapData, newHashes);
546
+ result.updated += updateResult;
547
+ } else {
548
+ const updateResult = await this.pushSkillUpdate(client, customer, change, mapData, newHashes);
549
+ result.updated += updateResult;
550
+ }
495
551
  } else if (change.operation === 'created') {
496
- // Create new entity
497
552
  const createResult = await this.pushNewEntity(client, customer, change, mapData, newHashes);
498
553
  result.created += createResult;
499
554
  }
@@ -563,6 +618,40 @@ export class ProjectSyncStrategy implements ISyncStrategy<ProjectMeta, LocalProj
563
618
  return 1;
564
619
  }
565
620
 
621
+ /**
622
+ * Push a library skill update
623
+ * Path: newo_customers/{customer}/projects/{project}/libraries/{lib}/{skill}/{skill}.jinja
624
+ */
625
+ private async pushLibrarySkillUpdate(
626
+ client: AxiosInstance,
627
+ change: ChangeItem<LocalProjectData>,
628
+ mapData: ProjectMap,
629
+ newHashes: HashStore
630
+ ): Promise<number> {
631
+ const pathParts = change.path.split('/');
632
+ const skillIdn = pathParts[pathParts.length - 2] || '';
633
+ const libIdn = pathParts[pathParts.length - 3] || '';
634
+ const projectIdn = pathParts[pathParts.length - 5] || '';
635
+
636
+ const projectData = mapData.projects[projectIdn];
637
+ const libData = projectData?.libraries?.[libIdn];
638
+ const skillData = libData?.skills[skillIdn];
639
+
640
+ if (!skillData || !libData) {
641
+ throw new Error(`Library skill ${skillIdn} not found in project map`);
642
+ }
643
+
644
+ const content = await fs.readFile(change.path, 'utf8');
645
+
646
+ await updateLibrarySkill(client, libData.id, skillData.id, {
647
+ prompt_script: content,
648
+ });
649
+
650
+ newHashes[change.path] = sha256(content);
651
+ this.logger.info(`Pushed library skill: ${libIdn}/${skillIdn}`);
652
+ return 1;
653
+ }
654
+
566
655
  /**
567
656
  * Push a new entity
568
657
  */
@@ -616,7 +705,7 @@ export class ProjectSyncStrategy implements ISyncStrategy<ProjectMeta, LocalProj
616
705
  const hashes = await loadHashes(customer.idn);
617
706
  const mapData = await fs.readJson(mapFile) as ProjectMap;
618
707
 
619
- // Scan for changed skill scripts
708
+ // Scan for changed flow skill scripts
620
709
  for (const [projectIdn, projectData] of Object.entries(mapData.projects)) {
621
710
  for (const [agentIdn, agentData] of Object.entries(projectData.agents)) {
622
711
  for (const [flowIdn, flowData] of Object.entries(agentData.flows)) {
@@ -629,7 +718,7 @@ export class ProjectSyncStrategy implements ISyncStrategy<ProjectMeta, LocalProj
629
718
 
630
719
  if (storedHash !== currentHash) {
631
720
  changes.push({
632
- item: {} as LocalProjectData, // Simplified for now
721
+ item: {} as LocalProjectData,
633
722
  operation: 'modified',
634
723
  path: skillFile.filePath
635
724
  });
@@ -638,6 +727,31 @@ export class ProjectSyncStrategy implements ISyncStrategy<ProjectMeta, LocalProj
638
727
  }
639
728
  }
640
729
  }
730
+
731
+ // Scan for changed library skill scripts
732
+ if (projectData.libraries) {
733
+ for (const [libIdn, libData] of Object.entries(projectData.libraries)) {
734
+ for (const [skillIdn, skillMeta] of Object.entries(libData.skills)) {
735
+ const scriptPath = librarySkillScriptPath(
736
+ customer.idn, projectIdn, libIdn, skillIdn, skillMeta.runner_type
737
+ );
738
+
739
+ if (await fs.pathExists(scriptPath)) {
740
+ const content = await fs.readFile(scriptPath, 'utf8');
741
+ const currentHash = sha256(content);
742
+ const storedHash = hashes[scriptPath];
743
+
744
+ if (storedHash !== currentHash) {
745
+ changes.push({
746
+ item: {} as LocalProjectData,
747
+ operation: 'modified',
748
+ path: scriptPath
749
+ });
750
+ }
751
+ }
752
+ }
753
+ }
754
+ }
641
755
  }
642
756
 
643
757
  return changes;