newo 1.9.2 → 2.0.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/CHANGELOG.md +116 -0
- package/README.md +68 -20
- package/dist/cli/commands/conversations.d.ts +3 -0
- package/dist/cli/commands/conversations.js +38 -0
- package/dist/cli/commands/help.d.ts +5 -0
- package/dist/cli/commands/help.js +50 -0
- package/dist/cli/commands/import-akb.d.ts +3 -0
- package/dist/cli/commands/import-akb.js +62 -0
- package/dist/cli/commands/list-customers.d.ts +3 -0
- package/dist/cli/commands/list-customers.js +13 -0
- package/dist/cli/commands/meta.d.ts +3 -0
- package/dist/cli/commands/meta.js +19 -0
- package/dist/cli/commands/pull-attributes.d.ts +3 -0
- package/dist/cli/commands/pull-attributes.js +16 -0
- package/dist/cli/commands/pull.d.ts +3 -0
- package/dist/cli/commands/pull.js +34 -0
- package/dist/cli/commands/push.d.ts +3 -0
- package/dist/cli/commands/push.js +39 -0
- package/dist/cli/commands/status.d.ts +3 -0
- package/dist/cli/commands/status.js +22 -0
- package/dist/cli/customer-selection.d.ts +23 -0
- package/dist/cli/customer-selection.js +110 -0
- package/dist/cli/errors.d.ts +9 -0
- package/dist/cli/errors.js +111 -0
- package/dist/cli.js +66 -463
- package/dist/fsutil.js +1 -1
- package/dist/sync/attributes.d.ts +7 -0
- package/dist/sync/attributes.js +90 -0
- package/dist/sync/conversations.d.ts +7 -0
- package/dist/sync/conversations.js +218 -0
- package/dist/sync/metadata.d.ts +8 -0
- package/dist/sync/metadata.js +124 -0
- package/dist/sync/projects.d.ts +13 -0
- package/dist/sync/projects.js +283 -0
- package/dist/sync/push.d.ts +7 -0
- package/dist/sync/push.js +171 -0
- package/dist/sync/skill-files.d.ts +42 -0
- package/dist/sync/skill-files.js +121 -0
- package/dist/sync/status.d.ts +6 -0
- package/dist/sync/status.js +247 -0
- package/dist/sync.d.ts +10 -8
- package/dist/sync.js +12 -1226
- package/dist/types.d.ts +0 -1
- package/package.json +2 -2
- package/src/cli/commands/conversations.ts +47 -0
- package/src/cli/commands/help.ts +50 -0
- package/src/cli/commands/import-akb.ts +71 -0
- package/src/cli/commands/list-customers.ts +14 -0
- package/src/cli/commands/meta.ts +26 -0
- package/src/cli/commands/pull-attributes.ts +23 -0
- package/src/cli/commands/pull.ts +43 -0
- package/src/cli/commands/push.ts +47 -0
- package/src/cli/commands/status.ts +30 -0
- package/src/cli/customer-selection.ts +135 -0
- package/src/cli/errors.ts +111 -0
- package/src/cli.ts +77 -471
- package/src/fsutil.ts +1 -1
- package/src/sync/attributes.ts +110 -0
- package/src/sync/conversations.ts +257 -0
- package/src/sync/metadata.ts +153 -0
- package/src/sync/projects.ts +359 -0
- package/src/sync/push.ts +200 -0
- package/src/sync/skill-files.ts +176 -0
- package/src/sync/status.ts +277 -0
- package/src/sync.ts +14 -1418
- package/src/types.ts +0 -1
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status checking module
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'fs-extra';
|
|
5
|
+
import yaml from 'js-yaml';
|
|
6
|
+
import { sha256, loadHashes } from '../hash.js';
|
|
7
|
+
import { ensureState, mapPath, skillMetadataPath, customerAttributesPath, customerAttributesBackupPath, flowsYamlPath } from '../fsutil.js';
|
|
8
|
+
import { validateSkillFolder, getSingleSkillFile } from './skill-files.js';
|
|
9
|
+
// Type guards for project map formats
|
|
10
|
+
function isProjectMap(x) {
|
|
11
|
+
return typeof x === 'object' && x !== null && 'projects' in x;
|
|
12
|
+
}
|
|
13
|
+
function isLegacyProjectMap(x) {
|
|
14
|
+
return typeof x === 'object' && x !== null && 'projectId' in x && 'agents' in x;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Check status of files for a customer
|
|
18
|
+
*/
|
|
19
|
+
export async function status(customer, verbose = false) {
|
|
20
|
+
await ensureState(customer.idn);
|
|
21
|
+
if (!(await fs.pathExists(mapPath(customer.idn)))) {
|
|
22
|
+
console.log(`No map for customer ${customer.idn}. Run \`newo pull --customer ${customer.idn}\` first.`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (verbose)
|
|
26
|
+
console.log(`📋 Loading project mapping and hashes for customer ${customer.idn}...`);
|
|
27
|
+
const idMapData = await fs.readJson(mapPath(customer.idn));
|
|
28
|
+
const hashes = await loadHashes(customer.idn);
|
|
29
|
+
let dirty = 0;
|
|
30
|
+
// Handle both old single-project format and new multi-project format with type guards
|
|
31
|
+
const projects = isProjectMap(idMapData) && idMapData.projects
|
|
32
|
+
? idMapData.projects
|
|
33
|
+
: isLegacyProjectMap(idMapData)
|
|
34
|
+
? { '': idMapData }
|
|
35
|
+
: (() => { throw new Error('Invalid project map format'); })();
|
|
36
|
+
for (const [projectIdn, projectData] of Object.entries(projects)) {
|
|
37
|
+
if (verbose && projectIdn)
|
|
38
|
+
console.log(`📁 Checking project: ${projectIdn}`);
|
|
39
|
+
for (const [agentIdn, agentObj] of Object.entries(projectData.agents)) {
|
|
40
|
+
if (verbose)
|
|
41
|
+
console.log(` 📁 Checking agent: ${agentIdn}`);
|
|
42
|
+
for (const [flowIdn, flowObj] of Object.entries(agentObj.flows)) {
|
|
43
|
+
if (verbose)
|
|
44
|
+
console.log(` 📁 Checking flow: ${flowIdn}`);
|
|
45
|
+
for (const [skillIdn] of Object.entries(flowObj.skills)) {
|
|
46
|
+
// Validate skill folder and show warnings
|
|
47
|
+
const validation = await validateSkillFolder(customer.idn, projectIdn, agentIdn, flowIdn, skillIdn);
|
|
48
|
+
if (!validation.isValid) {
|
|
49
|
+
// Show warnings and errors
|
|
50
|
+
validation.errors.forEach(error => {
|
|
51
|
+
console.error(`❌ ${error}`);
|
|
52
|
+
});
|
|
53
|
+
validation.warnings.forEach(warning => {
|
|
54
|
+
console.warn(`⚠️ ${warning}`);
|
|
55
|
+
});
|
|
56
|
+
if (validation.files.length > 1) {
|
|
57
|
+
console.warn(`⚠️ Multiple script files in skill ${skillIdn}:`);
|
|
58
|
+
validation.files.forEach(file => {
|
|
59
|
+
console.warn(` • ${file.fileName}`);
|
|
60
|
+
});
|
|
61
|
+
console.warn(` Status check skipped - please keep only one script file.`);
|
|
62
|
+
}
|
|
63
|
+
else if (validation.files.length === 0) {
|
|
64
|
+
console.log(`D ${skillIdn}/ (no script files)`);
|
|
65
|
+
dirty++;
|
|
66
|
+
}
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
// Get the single valid script file
|
|
70
|
+
const skillFile = await getSingleSkillFile(customer.idn, projectIdn, agentIdn, flowIdn, skillIdn);
|
|
71
|
+
if (!skillFile) {
|
|
72
|
+
console.log(`D ${skillIdn}/ (no valid script file)`);
|
|
73
|
+
dirty++;
|
|
74
|
+
if (verbose)
|
|
75
|
+
console.log(` ❌ No valid script file found: ${skillIdn}`);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const content = skillFile.content;
|
|
79
|
+
const currentPath = skillFile.filePath;
|
|
80
|
+
const h = sha256(content);
|
|
81
|
+
const oldHash = hashes[currentPath];
|
|
82
|
+
if (verbose) {
|
|
83
|
+
console.log(` 📄 ${currentPath}`);
|
|
84
|
+
console.log(` Old hash: ${oldHash || 'none'}`);
|
|
85
|
+
console.log(` New hash: ${h}`);
|
|
86
|
+
}
|
|
87
|
+
if (oldHash !== h) {
|
|
88
|
+
console.log(`M ${currentPath}`);
|
|
89
|
+
dirty++;
|
|
90
|
+
if (verbose)
|
|
91
|
+
console.log(` 🔄 Modified: ${skillFile.fileName}`);
|
|
92
|
+
}
|
|
93
|
+
else if (verbose) {
|
|
94
|
+
console.log(` ✓ Unchanged: ${skillFile.fileName}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Check metadata.yaml files for changes
|
|
98
|
+
for (const [skillIdn] of Object.entries(flowObj.skills)) {
|
|
99
|
+
const metadataPath = projectIdn ?
|
|
100
|
+
skillMetadataPath(customer.idn, projectIdn, agentIdn, flowIdn, skillIdn) :
|
|
101
|
+
skillMetadataPath(customer.idn, '', agentIdn, flowIdn, skillIdn);
|
|
102
|
+
if (await fs.pathExists(metadataPath)) {
|
|
103
|
+
const metadataContent = await fs.readFile(metadataPath, 'utf8');
|
|
104
|
+
const h = sha256(metadataContent);
|
|
105
|
+
const oldHash = hashes[metadataPath];
|
|
106
|
+
if (verbose) {
|
|
107
|
+
console.log(` 📄 ${metadataPath}`);
|
|
108
|
+
console.log(` Old hash: ${oldHash || 'none'}`);
|
|
109
|
+
console.log(` New hash: ${h}`);
|
|
110
|
+
}
|
|
111
|
+
if (oldHash !== h) {
|
|
112
|
+
console.log(`M ${metadataPath}`);
|
|
113
|
+
dirty++;
|
|
114
|
+
// Show which metadata fields changed
|
|
115
|
+
try {
|
|
116
|
+
const newMetadata = yaml.load(metadataContent);
|
|
117
|
+
console.log(` 📊 Metadata changed for skill: ${skillIdn}`);
|
|
118
|
+
if (newMetadata?.title) {
|
|
119
|
+
console.log(` • Title: ${newMetadata.title}`);
|
|
120
|
+
}
|
|
121
|
+
if (newMetadata?.runner_type) {
|
|
122
|
+
console.log(` • Runner: ${newMetadata.runner_type}`);
|
|
123
|
+
}
|
|
124
|
+
if (newMetadata?.model) {
|
|
125
|
+
console.log(` • Model: ${newMetadata.model.provider_idn}/${newMetadata.model.model_idn}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch (e) {
|
|
129
|
+
if (verbose)
|
|
130
|
+
console.log(` 🔄 Modified: metadata.yaml`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else if (verbose) {
|
|
134
|
+
console.log(` ✓ Unchanged: ${metadataPath}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// Check attributes file for changes
|
|
142
|
+
try {
|
|
143
|
+
const attributesFile = customerAttributesPath(customer.idn);
|
|
144
|
+
if (await fs.pathExists(attributesFile)) {
|
|
145
|
+
const content = await fs.readFile(attributesFile, 'utf8');
|
|
146
|
+
const h = sha256(content);
|
|
147
|
+
const oldHash = hashes[attributesFile];
|
|
148
|
+
if (verbose) {
|
|
149
|
+
console.log(`📄 ${attributesFile}`);
|
|
150
|
+
console.log(` Old hash: ${oldHash || 'none'}`);
|
|
151
|
+
console.log(` New hash: ${h}`);
|
|
152
|
+
}
|
|
153
|
+
if (oldHash !== h) {
|
|
154
|
+
console.log(`M ${attributesFile}`);
|
|
155
|
+
dirty++;
|
|
156
|
+
// Show which attributes changed by comparing with backup
|
|
157
|
+
try {
|
|
158
|
+
const attributesBackupFile = customerAttributesBackupPath(customer.idn);
|
|
159
|
+
if (await fs.pathExists(attributesBackupFile)) {
|
|
160
|
+
const backupContent = await fs.readFile(attributesBackupFile, 'utf8');
|
|
161
|
+
const parseYaml = (content) => {
|
|
162
|
+
let yamlContent = content.replace(/!enum "([^"]+)"/g, '"$1"');
|
|
163
|
+
return yaml.load(yamlContent);
|
|
164
|
+
};
|
|
165
|
+
const currentData = parseYaml(content);
|
|
166
|
+
const backupData = parseYaml(backupContent);
|
|
167
|
+
if (currentData?.attributes && backupData?.attributes) {
|
|
168
|
+
const currentAttrs = new Map(currentData.attributes.map(attr => [attr.idn, attr]));
|
|
169
|
+
const backupAttrs = new Map(backupData.attributes.map(attr => [attr.idn, attr]));
|
|
170
|
+
const changedAttributes = [];
|
|
171
|
+
for (const [idn, currentAttr] of currentAttrs) {
|
|
172
|
+
const backupAttr = backupAttrs.get(idn);
|
|
173
|
+
const hasChanged = !backupAttr ||
|
|
174
|
+
currentAttr.value !== backupAttr.value ||
|
|
175
|
+
currentAttr.title !== backupAttr.title ||
|
|
176
|
+
currentAttr.description !== backupAttr.description ||
|
|
177
|
+
currentAttr.group !== backupAttr.group ||
|
|
178
|
+
currentAttr.is_hidden !== backupAttr.is_hidden;
|
|
179
|
+
if (hasChanged) {
|
|
180
|
+
changedAttributes.push(idn);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (changedAttributes.length > 0) {
|
|
184
|
+
console.log(` 📊 Changed attributes (${changedAttributes.length}):`);
|
|
185
|
+
changedAttributes.slice(0, 5).forEach(idn => {
|
|
186
|
+
const current = currentAttrs.get(idn);
|
|
187
|
+
console.log(` • ${idn}: ${current?.title || 'No title'}`);
|
|
188
|
+
});
|
|
189
|
+
if (changedAttributes.length > 5) {
|
|
190
|
+
console.log(` ... and ${changedAttributes.length - 5} more`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
catch (e) {
|
|
197
|
+
// Fallback to simple message if diff analysis fails
|
|
198
|
+
}
|
|
199
|
+
if (verbose)
|
|
200
|
+
console.log(` 🔄 Modified: attributes.yaml`);
|
|
201
|
+
}
|
|
202
|
+
else if (verbose) {
|
|
203
|
+
console.log(` ✓ Unchanged: attributes.yaml`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
if (verbose)
|
|
209
|
+
console.log(`⚠️ Error checking attributes: ${error instanceof Error ? error.message : String(error)}`);
|
|
210
|
+
}
|
|
211
|
+
// Check flows.yaml file for changes
|
|
212
|
+
const flowsFile = flowsYamlPath(customer.idn);
|
|
213
|
+
if (await fs.pathExists(flowsFile)) {
|
|
214
|
+
try {
|
|
215
|
+
const flowsContent = await fs.readFile(flowsFile, 'utf8');
|
|
216
|
+
const h = sha256(flowsContent);
|
|
217
|
+
const oldHash = hashes[flowsFile];
|
|
218
|
+
if (verbose) {
|
|
219
|
+
console.log(`📄 flows.yaml`);
|
|
220
|
+
console.log(` Old hash: ${oldHash || 'none'}`);
|
|
221
|
+
console.log(` New hash: ${h}`);
|
|
222
|
+
}
|
|
223
|
+
if (oldHash !== h) {
|
|
224
|
+
console.log(`M ${flowsFile}`);
|
|
225
|
+
dirty++;
|
|
226
|
+
if (verbose) {
|
|
227
|
+
const flowsStats = await fs.stat(flowsFile);
|
|
228
|
+
console.log(` 🔄 Modified: flows.yaml`);
|
|
229
|
+
console.log(` 📊 Size: ${(flowsStats.size / 1024).toFixed(1)}KB`);
|
|
230
|
+
console.log(` 📅 Last modified: ${flowsStats.mtime.toISOString()}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
else if (verbose) {
|
|
234
|
+
const flowsStats = await fs.stat(flowsFile);
|
|
235
|
+
console.log(` ✓ Unchanged: flows.yaml`);
|
|
236
|
+
console.log(` 📅 Last modified: ${flowsStats.mtime.toISOString()}`);
|
|
237
|
+
console.log(` 📊 Size: ${(flowsStats.size / 1024).toFixed(1)}KB`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
if (verbose)
|
|
242
|
+
console.log(`⚠️ Error checking flows.yaml: ${error instanceof Error ? error.message : String(error)}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
console.log(dirty ? `${dirty} changed file(s).` : 'Clean.');
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=status.js.map
|
package/dist/sync.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
export
|
|
6
|
-
export
|
|
7
|
-
export
|
|
8
|
-
export
|
|
1
|
+
/**
|
|
2
|
+
* NEWO CLI Sync Operations - Modular architecture entry point
|
|
3
|
+
*/
|
|
4
|
+
export { saveCustomerAttributes } from './sync/attributes.js';
|
|
5
|
+
export { pullConversations } from './sync/conversations.js';
|
|
6
|
+
export { status } from './sync/status.js';
|
|
7
|
+
export { pullSingleProject, pullAll } from './sync/projects.js';
|
|
8
|
+
export { pushChanged } from './sync/push.js';
|
|
9
|
+
export { generateFlowsYaml } from './sync/metadata.js';
|
|
10
|
+
export { isProjectMap, isLegacyProjectMap } from './sync/projects.js';
|
|
9
11
|
//# sourceMappingURL=sync.d.ts.map
|