newo 3.6.1 → 3.6.2

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 CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.6.2] - 2026-04-23
11
+
12
+ ### Fixed
13
+
14
+ - **`attributes.yaml` YAML escaping**: The attribute serializer previously ran `.replace(/\\"/g, '"')` as a "prettify" post-processing step, which stripped legitimate YAML escape characters from double-quoted scalars. Values containing double quotes (e.g. `["+37410333310"]`) were written as invalid YAML like `value: "["+37410333310"]"` — rejected by `yaml.load` with `bad indentation of a mapping entry`. This made `newo push --format newo_v2` unable to parse customer/project attribute files at all, and long compiled values (e.g. AMI) broke on the first embedded quote. The serializer now uses the existing `patchYamlToPyyaml` post-processor (already used on the V2 path) to convert JSON-like double-quoted values to single-quoted form (`value: '["+37410333310"]'`) and handle long-line wrapping in pyyaml style. Fix applied in both `src/sync/attributes.ts` and `src/domain/strategies/sync/AttributeSyncStrategy.ts`. Added 11 round-trip regression tests.
15
+
10
16
  ## [3.6.1] - 2026-04-14
11
17
 
12
18
  ### Documentation
@@ -994,7 +1000,8 @@ Another Item: $Price [Modifiers: modifier3]
994
1000
  - GitHub Actions CI/CD integration
995
1001
  - Robust authentication with token refresh
996
1002
 
997
- [Unreleased]: https://github.com/sabbah13/newo-cli/compare/v3.3.0...HEAD
1003
+ [Unreleased]: https://github.com/sabbah13/newo-cli/compare/v3.6.2...HEAD
1004
+ [3.6.2]: https://github.com/sabbah13/newo-cli/compare/v3.6.1...v3.6.2
998
1005
  [3.3.0]: https://github.com/sabbah13/newo-cli/compare/v3.2.0...v3.3.0
999
1006
  [3.2.0]: https://github.com/sabbah13/newo-cli/compare/v3.1.0...v3.2.0
1000
1007
  [3.1.0]: https://github.com/sabbah13/newo-cli/compare/v3.0.0...v3.1.0
@@ -14,6 +14,7 @@ import yaml from 'js-yaml';
14
14
  import path from 'path';
15
15
  import { getCustomerAttributes, getProjectAttributes, updateCustomerAttribute, updateProjectAttribute, listProjects } from '../../../api.js';
16
16
  import { writeFileSafe, customerAttributesPath, customerAttributesMapPath } from '../../../fsutil.js';
17
+ import { patchYamlToPyyaml } from '../../../format/yaml-patch.js';
17
18
  import { sha256, saveHashes, loadHashes } from '../../../hash.js';
18
19
  /**
19
20
  * AttributeSyncStrategy - Handles attribute synchronization
@@ -171,18 +172,20 @@ export class AttributeSyncStrategy {
171
172
  ...attr,
172
173
  value_type: `__ENUM_PLACEHOLDER_${attr.value_type}__`
173
174
  }));
175
+ // Emit YAML without folding/wrapping; patchYamlToPyyaml handles long-line
176
+ // wrapping and converts JSON-like double-quoted values to single-quoted
177
+ // (so strings containing `"` stay valid YAML on reload).
174
178
  let yamlContent = yaml.dump({ attributes: attributesWithPlaceholders }, {
175
179
  indent: 2,
176
180
  quotingType: '"',
177
181
  forceQuotes: false,
178
- lineWidth: 80,
182
+ lineWidth: -1,
179
183
  noRefs: true,
180
184
  sortKeys: false,
181
- flowLevel: -1
185
+ flowLevel: -1,
182
186
  });
183
- // Replace placeholders with enum syntax
184
187
  yamlContent = yamlContent.replace(/__ENUM_PLACEHOLDER_(\w+)__/g, '!enum "AttributeValueTypes.$1"');
185
- yamlContent = yamlContent.replace(/\\"/g, '"');
188
+ yamlContent = patchYamlToPyyaml(yamlContent);
186
189
  return yamlContent;
187
190
  }
188
191
  /**
@@ -6,6 +6,7 @@ import { writeFileSafe, customerAttributesPath, customerAttributesMapPath, custo
6
6
  import path from 'path';
7
7
  import fs from 'fs-extra';
8
8
  import yaml from 'js-yaml';
9
+ import { patchYamlToPyyaml } from '../format/yaml-patch.js';
9
10
  /**
10
11
  * Save customer attributes to YAML format and return content for hashing
11
12
  */
@@ -55,23 +56,22 @@ export async function saveCustomerAttributes(client, customer, verbose = false)
55
56
  const attributesYaml = {
56
57
  attributes: cleanAttributes
57
58
  };
58
- // Configure YAML output to match reference format exactly
59
+ // Emit YAML without folding/wrapping; patchYamlToPyyaml handles long-line
60
+ // wrapping and converts JSON-like double-quoted values to single-quoted
61
+ // (so strings containing `"` stay valid YAML on reload).
59
62
  let yamlContent = yaml.dump(attributesYaml, {
60
63
  indent: 2,
61
64
  quotingType: '"',
62
65
  forceQuotes: false,
63
- lineWidth: 80, // Wrap long lines to match reference format
66
+ lineWidth: -1,
64
67
  noRefs: true,
65
68
  sortKeys: false,
66
- flowLevel: -1, // Never use flow syntax
67
- styles: {
68
- '!!str': 'folded' // Use folded style for better line wrapping of long strings
69
- }
69
+ flowLevel: -1,
70
70
  });
71
- // Post-process to fix enum format and improve JSON string formatting
71
+ // Post-process to fix enum format
72
72
  yamlContent = yamlContent.replace(/__ENUM_PLACEHOLDER_(\w+)__/g, '!enum "AttributeValueTypes.$1"');
73
- // Fix JSON string formatting to match reference (remove escape characters)
74
- yamlContent = yamlContent.replace(/\\"/g, '"');
73
+ // Convert JSON-like double-quoted values to single-quoted and wrap long lines
74
+ yamlContent = patchYamlToPyyaml(yamlContent);
75
75
  // Save all files: attributes.yaml, ID mapping, and backup for diff tracking
76
76
  await writeFileSafe(customerAttributesPath(customer.idn), yamlContent);
77
77
  await writeFileSafe(customerAttributesMapPath(customer.idn), JSON.stringify(idMapping, null, 2));
@@ -139,19 +139,19 @@ export async function saveProjectAttributes(client, customer, projectId, project
139
139
  const attributesYaml = {
140
140
  attributes: cleanAttributes
141
141
  };
142
- // Configure YAML output
142
+ // Emit YAML without folding/wrapping; patchYamlToPyyaml handles long-line
143
+ // wrapping and converts JSON-like double-quoted values to single-quoted.
143
144
  let yamlContent = yaml.dump(attributesYaml, {
144
145
  indent: 2,
145
146
  quotingType: '"',
146
147
  forceQuotes: false,
147
- lineWidth: 80,
148
+ lineWidth: -1,
148
149
  noRefs: true,
149
150
  sortKeys: false,
150
- flowLevel: -1
151
+ flowLevel: -1,
151
152
  });
152
- // Post-process to fix enum format
153
153
  yamlContent = yamlContent.replace(/__ENUM_PLACEHOLDER_(\w+)__/g, '!enum "AttributeValueTypes.$1"');
154
- yamlContent = yamlContent.replace(/\\"/g, '"');
154
+ yamlContent = patchYamlToPyyaml(yamlContent);
155
155
  // Save to project directory
156
156
  const customerDir = path.join(process.cwd(), 'newo_customers', customer.idn);
157
157
  const projectDir = path.join(customerDir, 'projects', projectIdn);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "newo",
3
- "version": "3.6.1",
3
+ "version": "3.6.2",
4
4
  "description": "NEWO CLI: Professional command-line tool with modular architecture for NEWO AI Agent development. Features account migration, integration management, webhook automation, AKB knowledge base, project attributes, sandbox testing, IDN-based file management, real-time progress tracking, intelligent sync operations, and comprehensive multi-customer support.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -38,6 +38,7 @@ import {
38
38
  customerAttributesPath,
39
39
  customerAttributesMapPath
40
40
  } from '../../../fsutil.js';
41
+ import { patchYamlToPyyaml } from '../../../format/yaml-patch.js';
41
42
  import { sha256, saveHashes, loadHashes } from '../../../hash.js';
42
43
 
43
44
  /**
@@ -251,19 +252,21 @@ export class AttributeSyncStrategy implements ISyncStrategy<CustomerAttributesRe
251
252
  value_type: `__ENUM_PLACEHOLDER_${attr.value_type}__`
252
253
  }));
253
254
 
255
+ // Emit YAML without folding/wrapping; patchYamlToPyyaml handles long-line
256
+ // wrapping and converts JSON-like double-quoted values to single-quoted
257
+ // (so strings containing `"` stay valid YAML on reload).
254
258
  let yamlContent = yaml.dump({ attributes: attributesWithPlaceholders }, {
255
259
  indent: 2,
256
260
  quotingType: '"',
257
261
  forceQuotes: false,
258
- lineWidth: 80,
262
+ lineWidth: -1,
259
263
  noRefs: true,
260
264
  sortKeys: false,
261
- flowLevel: -1
265
+ flowLevel: -1,
262
266
  });
263
267
 
264
- // Replace placeholders with enum syntax
265
268
  yamlContent = yamlContent.replace(/__ENUM_PLACEHOLDER_(\w+)__/g, '!enum "AttributeValueTypes.$1"');
266
- yamlContent = yamlContent.replace(/\\"/g, '"');
269
+ yamlContent = patchYamlToPyyaml(yamlContent);
267
270
 
268
271
  return yamlContent;
269
272
  }
@@ -11,6 +11,7 @@ import {
11
11
  import path from 'path';
12
12
  import fs from 'fs-extra';
13
13
  import yaml from 'js-yaml';
14
+ import { patchYamlToPyyaml } from '../format/yaml-patch.js';
14
15
  import type { AxiosInstance } from 'axios';
15
16
  import type { CustomerConfig } from '../types.js';
16
17
 
@@ -72,25 +73,24 @@ export async function saveCustomerAttributes(
72
73
  attributes: cleanAttributes
73
74
  };
74
75
 
75
- // Configure YAML output to match reference format exactly
76
+ // Emit YAML without folding/wrapping; patchYamlToPyyaml handles long-line
77
+ // wrapping and converts JSON-like double-quoted values to single-quoted
78
+ // (so strings containing `"` stay valid YAML on reload).
76
79
  let yamlContent = yaml.dump(attributesYaml, {
77
80
  indent: 2,
78
81
  quotingType: '"',
79
82
  forceQuotes: false,
80
- lineWidth: 80, // Wrap long lines to match reference format
83
+ lineWidth: -1,
81
84
  noRefs: true,
82
85
  sortKeys: false,
83
- flowLevel: -1, // Never use flow syntax
84
- styles: {
85
- '!!str': 'folded' // Use folded style for better line wrapping of long strings
86
- }
86
+ flowLevel: -1,
87
87
  });
88
88
 
89
- // Post-process to fix enum format and improve JSON string formatting
89
+ // Post-process to fix enum format
90
90
  yamlContent = yamlContent.replace(/__ENUM_PLACEHOLDER_(\w+)__/g, '!enum "AttributeValueTypes.$1"');
91
91
 
92
- // Fix JSON string formatting to match reference (remove escape characters)
93
- yamlContent = yamlContent.replace(/\\"/g, '"');
92
+ // Convert JSON-like double-quoted values to single-quoted and wrap long lines
93
+ yamlContent = patchYamlToPyyaml(yamlContent);
94
94
 
95
95
  // Save all files: attributes.yaml, ID mapping, and backup for diff tracking
96
96
  await writeFileSafe(customerAttributesPath(customer.idn), yamlContent);
@@ -172,20 +172,20 @@ export async function saveProjectAttributes(
172
172
  attributes: cleanAttributes
173
173
  };
174
174
 
175
- // Configure YAML output
175
+ // Emit YAML without folding/wrapping; patchYamlToPyyaml handles long-line
176
+ // wrapping and converts JSON-like double-quoted values to single-quoted.
176
177
  let yamlContent = yaml.dump(attributesYaml, {
177
178
  indent: 2,
178
179
  quotingType: '"',
179
180
  forceQuotes: false,
180
- lineWidth: 80,
181
+ lineWidth: -1,
181
182
  noRefs: true,
182
183
  sortKeys: false,
183
- flowLevel: -1
184
+ flowLevel: -1,
184
185
  });
185
186
 
186
- // Post-process to fix enum format
187
187
  yamlContent = yamlContent.replace(/__ENUM_PLACEHOLDER_(\w+)__/g, '!enum "AttributeValueTypes.$1"');
188
- yamlContent = yamlContent.replace(/\\"/g, '"');
188
+ yamlContent = patchYamlToPyyaml(yamlContent);
189
189
 
190
190
  // Save to project directory
191
191
  const customerDir = path.join(process.cwd(), 'newo_customers', customer.idn);