newo 3.1.0 ā 3.2.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 +139 -1
- package/dist/api.d.ts +14 -1
- package/dist/api.js +74 -0
- package/dist/cli/commands/help.js +20 -1
- package/dist/cli/commands/list-actions.d.ts +3 -0
- package/dist/cli/commands/list-actions.js +89 -0
- package/dist/cli/commands/profile.d.ts +3 -0
- package/dist/cli/commands/profile.js +62 -0
- package/dist/cli/commands/pull-akb.d.ts +3 -0
- package/dist/cli/commands/pull-akb.js +19 -0
- package/dist/cli/commands/pull-attributes.js +7 -0
- package/dist/cli/commands/pull-integrations.d.ts +3 -0
- package/dist/cli/commands/pull-integrations.js +19 -0
- package/dist/cli/commands/push-akb.d.ts +3 -0
- package/dist/cli/commands/push-akb.js +19 -0
- package/dist/cli/commands/push-integrations.d.ts +3 -0
- package/dist/cli/commands/push-integrations.js +19 -0
- package/dist/cli.js +24 -0
- package/dist/sync/akb.d.ts +14 -0
- package/dist/sync/akb.js +175 -0
- package/dist/sync/attributes.d.ts +19 -0
- package/dist/sync/attributes.js +221 -2
- package/dist/sync/integrations.d.ts +23 -0
- package/dist/sync/integrations.js +340 -0
- package/dist/sync/projects.js +171 -1
- package/dist/sync/push.js +15 -0
- package/dist/sync/skill-files.js +1 -1
- package/dist/sync/status.js +4 -2
- package/dist/types.d.ts +128 -0
- package/package.json +11 -3
- package/src/api.ts +132 -1
- package/src/cli/commands/help.ts +20 -1
- package/src/cli/commands/list-actions.ts +112 -0
- package/src/cli/commands/profile.ts +79 -0
- package/src/cli/commands/pull-akb.ts +27 -0
- package/src/cli/commands/pull-attributes.ts +8 -0
- package/src/cli/commands/pull-integrations.ts +27 -0
- package/src/cli/commands/push-akb.ts +27 -0
- package/src/cli/commands/push-integrations.ts +27 -0
- package/src/cli.ts +30 -0
- package/src/sync/akb.ts +205 -0
- package/src/sync/attributes.ts +269 -2
- package/src/sync/integrations.ts +403 -0
- package/src/sync/projects.ts +207 -1
- package/src/sync/push.ts +17 -0
- package/src/sync/skill-files.ts +1 -1
- package/src/sync/status.ts +4 -2
- package/src/types.ts +150 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AKB (Knowledge Base) synchronization module
|
|
3
|
+
* Handles pull/push of AKB articles for personas with agents
|
|
4
|
+
*/
|
|
5
|
+
import type { AxiosInstance } from 'axios';
|
|
6
|
+
/**
|
|
7
|
+
* Pull AKB articles for all personas linked to agents
|
|
8
|
+
*/
|
|
9
|
+
export declare function pullAkb(client: AxiosInstance, customerDir: string, verbose?: boolean): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* Push AKB articles from local files to NEWO platform
|
|
12
|
+
*/
|
|
13
|
+
export declare function pushAkb(client: AxiosInstance, customerDir: string, verbose?: boolean): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=akb.d.ts.map
|
package/dist/sync/akb.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AKB (Knowledge Base) synchronization module
|
|
3
|
+
* Handles pull/push of AKB articles for personas with agents
|
|
4
|
+
*/
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import fs from 'fs-extra';
|
|
7
|
+
import yaml from 'js-yaml';
|
|
8
|
+
import { searchPersonas, getAkbTopics, importAkbArticle } from '../api.js';
|
|
9
|
+
/**
|
|
10
|
+
* Pull AKB articles for all personas linked to agents
|
|
11
|
+
*/
|
|
12
|
+
export async function pullAkb(client, customerDir, verbose = false) {
|
|
13
|
+
if (verbose)
|
|
14
|
+
console.log('\nš Pulling AKB (Knowledge Base) from NEWO platform...\n');
|
|
15
|
+
// Create AKB directory
|
|
16
|
+
const akbDir = path.join(customerDir, 'akb');
|
|
17
|
+
await fs.ensureDir(akbDir);
|
|
18
|
+
// Fetch personas linked to agents
|
|
19
|
+
let allPersonas = [];
|
|
20
|
+
let page = 1;
|
|
21
|
+
const perPage = 30;
|
|
22
|
+
while (true) {
|
|
23
|
+
const response = await searchPersonas(client, true, page, perPage);
|
|
24
|
+
allPersonas = allPersonas.concat(response.items);
|
|
25
|
+
if (verbose)
|
|
26
|
+
console.log(`ā Fetched ${response.items.length} personas (page ${page}/${Math.ceil(response.metadata.total / perPage)})`);
|
|
27
|
+
if (response.items.length < perPage || allPersonas.length >= response.metadata.total) {
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
page++;
|
|
31
|
+
}
|
|
32
|
+
if (verbose)
|
|
33
|
+
console.log(`\nā Found ${allPersonas.length} personas linked to agents`);
|
|
34
|
+
let totalArticles = 0;
|
|
35
|
+
// Fetch AKB articles for each persona
|
|
36
|
+
for (const persona of allPersonas) {
|
|
37
|
+
if (verbose)
|
|
38
|
+
console.log(`\n š Processing persona: ${persona.name} (${persona.agent.idn})`);
|
|
39
|
+
// Fetch all AKB topics for this persona
|
|
40
|
+
let allTopicItems = [];
|
|
41
|
+
let topicPage = 1;
|
|
42
|
+
const topicsPerPage = 100;
|
|
43
|
+
while (true) {
|
|
44
|
+
try {
|
|
45
|
+
const topicsResponse = await getAkbTopics(client, persona.id, topicPage, topicsPerPage);
|
|
46
|
+
allTopicItems = allTopicItems.concat(topicsResponse.items);
|
|
47
|
+
if (verbose)
|
|
48
|
+
console.log(` ā Fetched ${topicsResponse.items.length} topics (page ${topicPage})`);
|
|
49
|
+
if (topicsResponse.items.length < topicsPerPage || allTopicItems.length >= topicsResponse.metadata.total) {
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
topicPage++;
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (verbose)
|
|
56
|
+
console.log(` ā Could not fetch topics: ${error.message}`);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (allTopicItems.length > 0) {
|
|
61
|
+
// Convert to YAML format (extract topic from each item)
|
|
62
|
+
const yamlTopics = allTopicItems.map(item => ({
|
|
63
|
+
topic_name: item.topic.topic_name,
|
|
64
|
+
topic_facts: [...item.topic.topic_facts],
|
|
65
|
+
confidence: item.topic.confidence,
|
|
66
|
+
source: item.topic.source,
|
|
67
|
+
created_at: item.topic.created_at,
|
|
68
|
+
updated_at: item.topic.updated_at,
|
|
69
|
+
labels: [...item.topic.labels],
|
|
70
|
+
topic_summary: item.topic.topic_summary
|
|
71
|
+
}));
|
|
72
|
+
// Save to persona-specific YAML file using agent IDN
|
|
73
|
+
const agentIdn = persona.agent.idn;
|
|
74
|
+
const akbFile = path.join(akbDir, `${agentIdn}.yaml`);
|
|
75
|
+
await fs.writeFile(akbFile, yaml.dump(yamlTopics, { lineWidth: -1 }));
|
|
76
|
+
if (verbose)
|
|
77
|
+
console.log(` ā Saved ${allTopicItems.length} articles ā ${agentIdn}.yaml`);
|
|
78
|
+
totalArticles += allTopicItems.length;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
if (verbose)
|
|
82
|
+
console.log(` ā¹ No AKB articles found for this persona`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (verbose) {
|
|
86
|
+
console.log(`\nā
Saved AKB articles for ${allPersonas.length} personas`);
|
|
87
|
+
console.log(` Total articles: ${totalArticles}\n`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Push AKB articles from local files to NEWO platform
|
|
92
|
+
*/
|
|
93
|
+
export async function pushAkb(client, customerDir, verbose = false) {
|
|
94
|
+
if (verbose)
|
|
95
|
+
console.log('\nš¤ Pushing AKB articles to NEWO platform...\n');
|
|
96
|
+
const akbDir = path.join(customerDir, 'akb');
|
|
97
|
+
// Check if AKB directory exists
|
|
98
|
+
if (!await fs.pathExists(akbDir)) {
|
|
99
|
+
if (verbose)
|
|
100
|
+
console.log('ā No akb directory found. Run pull-akb first.');
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
// Get personas linked to agents for ID mapping
|
|
104
|
+
let allPersonas = [];
|
|
105
|
+
let page = 1;
|
|
106
|
+
const perPage = 30;
|
|
107
|
+
while (true) {
|
|
108
|
+
const response = await searchPersonas(client, true, page, perPage);
|
|
109
|
+
allPersonas = allPersonas.concat(response.items);
|
|
110
|
+
if (response.items.length < perPage || allPersonas.length >= response.metadata.total) {
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
page++;
|
|
114
|
+
}
|
|
115
|
+
// Create persona mapping (agent.idn -> persona.id)
|
|
116
|
+
const personaMap = new Map();
|
|
117
|
+
allPersonas.forEach(persona => {
|
|
118
|
+
personaMap.set(persona.agent.idn, persona.id);
|
|
119
|
+
personaMap.set(persona.name, persona.id); // Also map by name as fallback
|
|
120
|
+
});
|
|
121
|
+
// Read AKB files
|
|
122
|
+
const akbFiles = await fs.readdir(akbDir);
|
|
123
|
+
let totalImported = 0;
|
|
124
|
+
let totalFailed = 0;
|
|
125
|
+
for (const file of akbFiles) {
|
|
126
|
+
if (!file.endsWith('.yaml'))
|
|
127
|
+
continue;
|
|
128
|
+
const fileBase = file.replace('.yaml', '');
|
|
129
|
+
const personaId = personaMap.get(fileBase);
|
|
130
|
+
if (!personaId) {
|
|
131
|
+
if (verbose)
|
|
132
|
+
console.log(`ā Persona not found for file: ${file}, skipping...`);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (verbose)
|
|
136
|
+
console.log(`\n š Processing: ${file}`);
|
|
137
|
+
// Read YAML file
|
|
138
|
+
const akbFile = path.join(akbDir, file);
|
|
139
|
+
const topics = yaml.load(await fs.readFile(akbFile, 'utf-8'));
|
|
140
|
+
if (!Array.isArray(topics)) {
|
|
141
|
+
if (verbose)
|
|
142
|
+
console.log(` ā Invalid YAML format, skipping...`);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
// Import each article
|
|
146
|
+
for (const topic of topics) {
|
|
147
|
+
try {
|
|
148
|
+
const articleData = {
|
|
149
|
+
topic_name: topic.topic_name,
|
|
150
|
+
topic_summary: topic.topic_summary,
|
|
151
|
+
topic_facts: topic.topic_facts,
|
|
152
|
+
confidence: topic.confidence,
|
|
153
|
+
source: topic.source,
|
|
154
|
+
labels: topic.labels,
|
|
155
|
+
persona_id: personaId
|
|
156
|
+
};
|
|
157
|
+
await importAkbArticle(client, articleData);
|
|
158
|
+
totalImported++;
|
|
159
|
+
if (verbose)
|
|
160
|
+
console.log(` ā Imported: ${topic.topic_name}`);
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
totalFailed++;
|
|
164
|
+
if (verbose)
|
|
165
|
+
console.error(` ā Failed to import ${topic.topic_name}: ${error.message}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (verbose) {
|
|
170
|
+
console.log(`\nā
Push completed:`);
|
|
171
|
+
console.log(` Imported: ${totalImported}`);
|
|
172
|
+
console.log(` Failed: ${totalFailed}\n`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=akb.js.map
|
|
@@ -4,4 +4,23 @@ import type { CustomerConfig } from '../types.js';
|
|
|
4
4
|
* Save customer attributes to YAML format and return content for hashing
|
|
5
5
|
*/
|
|
6
6
|
export declare function saveCustomerAttributes(client: AxiosInstance, customer: CustomerConfig, verbose?: boolean): Promise<string>;
|
|
7
|
+
/**
|
|
8
|
+
* Save project attributes to YAML format in project directory
|
|
9
|
+
*/
|
|
10
|
+
export declare function saveProjectAttributes(client: AxiosInstance, customer: CustomerConfig, projectId: string, projectIdn: string, verbose?: boolean): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Pull all project attributes for a customer
|
|
13
|
+
*/
|
|
14
|
+
export declare function pullAllProjectAttributes(client: AxiosInstance, customer: CustomerConfig, verbose?: boolean): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Push modified project attributes for a specific project
|
|
17
|
+
*/
|
|
18
|
+
export declare function pushProjectAttributes(client: AxiosInstance, customer: CustomerConfig, projectId: string, projectIdn: string, verbose?: boolean): Promise<number>;
|
|
19
|
+
/**
|
|
20
|
+
* Push all modified project attributes for all projects
|
|
21
|
+
*/
|
|
22
|
+
export declare function pushAllProjectAttributes(client: AxiosInstance, customer: CustomerConfig, projectsMap: Record<string, {
|
|
23
|
+
projectId: string;
|
|
24
|
+
projectIdn: string;
|
|
25
|
+
}>, verbose?: boolean): Promise<number>;
|
|
7
26
|
//# sourceMappingURL=attributes.d.ts.map
|
package/dist/sync/attributes.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Customer attributes synchronization module
|
|
2
|
+
* Customer and project attributes synchronization module
|
|
3
3
|
*/
|
|
4
|
-
import { getCustomerAttributes } from '../api.js';
|
|
4
|
+
import { getCustomerAttributes, getProjectAttributes, listProjects, updateProjectAttribute } from '../api.js';
|
|
5
5
|
import { writeFileSafe, customerAttributesPath, customerAttributesMapPath, customerAttributesBackupPath } from '../fsutil.js';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import fs from 'fs-extra';
|
|
6
8
|
import yaml from 'js-yaml';
|
|
7
9
|
/**
|
|
8
10
|
* Save customer attributes to YAML format and return content for hashing
|
|
@@ -87,4 +89,221 @@ export async function saveCustomerAttributes(client, customer, verbose = false)
|
|
|
87
89
|
throw error;
|
|
88
90
|
}
|
|
89
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Save project attributes to YAML format in project directory
|
|
94
|
+
*/
|
|
95
|
+
export async function saveProjectAttributes(client, customer, projectId, projectIdn, verbose = false) {
|
|
96
|
+
if (verbose)
|
|
97
|
+
console.log(` š Fetching project attributes for ${projectIdn}...`);
|
|
98
|
+
try {
|
|
99
|
+
const response = await getProjectAttributes(client, projectId, true); // Include hidden attributes
|
|
100
|
+
// API returns { groups: [...], attributes: [...] }
|
|
101
|
+
const attributes = response.attributes || response;
|
|
102
|
+
if (verbose)
|
|
103
|
+
console.log(` š¦ Found ${Array.isArray(attributes) ? attributes.length : 0} project attributes`);
|
|
104
|
+
if (!Array.isArray(attributes) || attributes.length === 0) {
|
|
105
|
+
if (verbose)
|
|
106
|
+
console.log(` ā¹ No project attributes found for ${projectIdn}`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
// Create ID mapping for push operations
|
|
110
|
+
const idMapping = {};
|
|
111
|
+
// Transform attributes to match format (no ID fields)
|
|
112
|
+
const cleanAttributes = attributes.map(attr => {
|
|
113
|
+
// Store ID mapping
|
|
114
|
+
if (attr.id) {
|
|
115
|
+
idMapping[attr.idn] = attr.id;
|
|
116
|
+
}
|
|
117
|
+
// Special handling for complex JSON string values
|
|
118
|
+
let processedValue = attr.value;
|
|
119
|
+
if (typeof attr.value === 'string' && attr.value.startsWith('[{') && attr.value.endsWith('}]')) {
|
|
120
|
+
try {
|
|
121
|
+
const parsed = JSON.parse(attr.value);
|
|
122
|
+
processedValue = JSON.stringify(parsed, null, 0);
|
|
123
|
+
}
|
|
124
|
+
catch (e) {
|
|
125
|
+
processedValue = attr.value;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
idn: attr.idn,
|
|
130
|
+
value: processedValue,
|
|
131
|
+
title: attr.title || "",
|
|
132
|
+
description: attr.description || "",
|
|
133
|
+
group: attr.group || "",
|
|
134
|
+
is_hidden: attr.is_hidden,
|
|
135
|
+
possible_values: attr.possible_values || [],
|
|
136
|
+
value_type: `__ENUM_PLACEHOLDER_${attr.value_type}__`
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
const attributesYaml = {
|
|
140
|
+
attributes: cleanAttributes
|
|
141
|
+
};
|
|
142
|
+
// Configure YAML output
|
|
143
|
+
let yamlContent = yaml.dump(attributesYaml, {
|
|
144
|
+
indent: 2,
|
|
145
|
+
quotingType: '"',
|
|
146
|
+
forceQuotes: false,
|
|
147
|
+
lineWidth: 80,
|
|
148
|
+
noRefs: true,
|
|
149
|
+
sortKeys: false,
|
|
150
|
+
flowLevel: -1
|
|
151
|
+
});
|
|
152
|
+
// Post-process to fix enum format
|
|
153
|
+
yamlContent = yamlContent.replace(/__ENUM_PLACEHOLDER_(\w+)__/g, '!enum "AttributeValueTypes.$1"');
|
|
154
|
+
yamlContent = yamlContent.replace(/\\"/g, '"');
|
|
155
|
+
// Save to project directory
|
|
156
|
+
const customerDir = path.join(process.cwd(), 'newo_customers', customer.idn);
|
|
157
|
+
const projectDir = path.join(customerDir, 'projects', projectIdn);
|
|
158
|
+
await fs.ensureDir(projectDir);
|
|
159
|
+
const attributesFile = path.join(projectDir, 'attributes.yaml');
|
|
160
|
+
const attributesMapFile = path.join(customerDir, '.newo', customer.idn, `project_${projectIdn}_attributes-map.json`);
|
|
161
|
+
await writeFileSafe(attributesFile, yamlContent);
|
|
162
|
+
await fs.ensureDir(path.dirname(attributesMapFile));
|
|
163
|
+
await writeFileSafe(attributesMapFile, JSON.stringify(idMapping, null, 2));
|
|
164
|
+
if (verbose) {
|
|
165
|
+
console.log(` ā Saved project attributes to projects/${projectIdn}/attributes.yaml`);
|
|
166
|
+
console.log(` ā Saved attribute ID mapping`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
if (verbose)
|
|
171
|
+
console.error(` ā Failed to fetch project attributes for ${projectIdn}:`, error.message);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Pull all project attributes for a customer
|
|
176
|
+
*/
|
|
177
|
+
export async function pullAllProjectAttributes(client, customer, verbose = false) {
|
|
178
|
+
if (verbose)
|
|
179
|
+
console.log(`\nš Fetching project attributes...`);
|
|
180
|
+
try {
|
|
181
|
+
// Get all projects for this customer
|
|
182
|
+
const projects = await listProjects(client);
|
|
183
|
+
if (verbose)
|
|
184
|
+
console.log(`ā Found ${projects.length} projects\n`);
|
|
185
|
+
for (const project of projects) {
|
|
186
|
+
await saveProjectAttributes(client, customer, project.id, project.idn, verbose);
|
|
187
|
+
}
|
|
188
|
+
if (verbose)
|
|
189
|
+
console.log(`\nā
Completed project attributes sync for ${projects.length} projects\n`);
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
console.error(`ā Failed to pull project attributes:`, error);
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Push modified project attributes for a specific project
|
|
198
|
+
*/
|
|
199
|
+
export async function pushProjectAttributes(client, customer, projectId, projectIdn, verbose = false) {
|
|
200
|
+
const customerDir = path.join(process.cwd(), 'newo_customers', customer.idn);
|
|
201
|
+
const attributesFile = path.join(customerDir, 'projects', projectIdn, 'attributes.yaml');
|
|
202
|
+
const attributesMapFile = path.join(customerDir, '.newo', customer.idn, `project_${projectIdn}_attributes-map.json`);
|
|
203
|
+
// Check if attributes file exists
|
|
204
|
+
if (!await fs.pathExists(attributesFile)) {
|
|
205
|
+
if (verbose)
|
|
206
|
+
console.log(` ā¹ No project attributes file for ${projectIdn}`);
|
|
207
|
+
return 0;
|
|
208
|
+
}
|
|
209
|
+
// Load local attributes
|
|
210
|
+
// Read as text and replace the custom !enum tags before parsing
|
|
211
|
+
let attributesContent = await fs.readFile(attributesFile, 'utf-8');
|
|
212
|
+
// Replace custom enum tags with the actual value
|
|
213
|
+
attributesContent = attributesContent.replace(/!enum "AttributeValueTypes\.(\w+)"/g, '$1');
|
|
214
|
+
const localData = yaml.load(attributesContent);
|
|
215
|
+
const localAttributes = localData.attributes || [];
|
|
216
|
+
// Load ID mapping
|
|
217
|
+
if (!await fs.pathExists(attributesMapFile)) {
|
|
218
|
+
if (verbose)
|
|
219
|
+
console.log(` ā No ID mapping found for project ${projectIdn}, skipping push`);
|
|
220
|
+
return 0;
|
|
221
|
+
}
|
|
222
|
+
const idMapping = JSON.parse(await fs.readFile(attributesMapFile, 'utf-8'));
|
|
223
|
+
// Fetch current remote attributes for comparison
|
|
224
|
+
const remoteResponse = await getProjectAttributes(client, projectId, true);
|
|
225
|
+
const remoteAttributes = remoteResponse.attributes || [];
|
|
226
|
+
// Create map of remote attributes by IDN
|
|
227
|
+
const remoteMap = new Map();
|
|
228
|
+
remoteAttributes.forEach(attr => remoteMap.set(attr.idn, attr));
|
|
229
|
+
let updatedCount = 0;
|
|
230
|
+
// Check each local attribute for changes
|
|
231
|
+
for (const localAttr of localAttributes) {
|
|
232
|
+
const attributeId = idMapping[localAttr.idn];
|
|
233
|
+
if (!attributeId) {
|
|
234
|
+
if (verbose)
|
|
235
|
+
console.log(` ā No ID mapping for attribute ${localAttr.idn}, skipping`);
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
const remoteAttr = remoteMap.get(localAttr.idn);
|
|
239
|
+
if (!remoteAttr) {
|
|
240
|
+
if (verbose)
|
|
241
|
+
console.log(` ā Attribute ${localAttr.idn} not found remotely, skipping`);
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
// Value type is already parsed (we removed !enum tags above)
|
|
245
|
+
const valueType = localAttr.value_type;
|
|
246
|
+
// Check if value changed
|
|
247
|
+
const localValue = String(localAttr.value || '');
|
|
248
|
+
const remoteValue = String(remoteAttr.value || '');
|
|
249
|
+
if (localValue !== remoteValue) {
|
|
250
|
+
if (verbose)
|
|
251
|
+
console.log(` š Updating project attribute: ${localAttr.idn}`);
|
|
252
|
+
try {
|
|
253
|
+
const attributeToUpdate = {
|
|
254
|
+
id: attributeId,
|
|
255
|
+
idn: localAttr.idn,
|
|
256
|
+
value: localAttr.value,
|
|
257
|
+
title: localAttr.title,
|
|
258
|
+
description: localAttr.description,
|
|
259
|
+
group: localAttr.group,
|
|
260
|
+
is_hidden: localAttr.is_hidden,
|
|
261
|
+
possible_values: localAttr.possible_values,
|
|
262
|
+
value_type: valueType
|
|
263
|
+
};
|
|
264
|
+
await updateProjectAttribute(client, projectId, attributeToUpdate);
|
|
265
|
+
if (verbose)
|
|
266
|
+
console.log(` ā
Updated: ${localAttr.idn} (${localAttr.title})`);
|
|
267
|
+
updatedCount++;
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
const errorDetail = error.response?.data || error.message;
|
|
271
|
+
console.error(` ā Failed to update ${localAttr.idn}: ${JSON.stringify(errorDetail)}`);
|
|
272
|
+
if (verbose) {
|
|
273
|
+
console.error(` API response:`, error.response?.status, error.response?.statusText);
|
|
274
|
+
console.error(` Endpoint tried: PUT /api/v1/project/attributes/${attributeId}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
else if (verbose) {
|
|
279
|
+
console.log(` ā No changes: ${localAttr.idn}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return updatedCount;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Push all modified project attributes for all projects
|
|
286
|
+
*/
|
|
287
|
+
export async function pushAllProjectAttributes(client, customer, projectsMap, verbose = false) {
|
|
288
|
+
if (verbose)
|
|
289
|
+
console.log(`\nš Checking project attributes for changes...`);
|
|
290
|
+
let totalUpdated = 0;
|
|
291
|
+
for (const [projectIdn, projectInfo] of Object.entries(projectsMap)) {
|
|
292
|
+
if (!projectIdn)
|
|
293
|
+
continue; // Skip empty project idn (legacy format)
|
|
294
|
+
if (verbose)
|
|
295
|
+
console.log(`\n š Project: ${projectIdn}`);
|
|
296
|
+
const updated = await pushProjectAttributes(client, customer, projectInfo.projectId, projectInfo.projectIdn || projectIdn, verbose);
|
|
297
|
+
totalUpdated += updated;
|
|
298
|
+
}
|
|
299
|
+
// Always show summary if changes were made
|
|
300
|
+
if (totalUpdated > 0) {
|
|
301
|
+
console.log(`\nā
Updated ${totalUpdated} project attribute(s)`);
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
if (verbose)
|
|
305
|
+
console.log(`\nā No project attribute changes to push`);
|
|
306
|
+
}
|
|
307
|
+
return totalUpdated;
|
|
308
|
+
}
|
|
90
309
|
//# sourceMappingURL=attributes.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration and connector synchronization module
|
|
3
|
+
* Handles pull/push of integrations and connectors to/from NEWO platform
|
|
4
|
+
*/
|
|
5
|
+
import type { AxiosInstance } from 'axios';
|
|
6
|
+
import type { IntegrationMetadata, ConnectorMetadata } from '../types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Pull all integrations and connectors from NEWO platform
|
|
9
|
+
*/
|
|
10
|
+
export declare function pullIntegrations(client: AxiosInstance, customerDir: string, verbose?: boolean): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Push integration changes from local files to NEWO platform
|
|
13
|
+
*/
|
|
14
|
+
export declare function pushIntegrations(client: AxiosInstance, customerDir: string, verbose?: boolean): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* List all local integrations from file system
|
|
17
|
+
*/
|
|
18
|
+
export declare function listLocalIntegrations(customerDir: string): Promise<IntegrationMetadata[]>;
|
|
19
|
+
/**
|
|
20
|
+
* Get connector details from local file
|
|
21
|
+
*/
|
|
22
|
+
export declare function getLocalConnector(customerDir: string, integrationIdn: string, connectorIdn: string): Promise<ConnectorMetadata | null>;
|
|
23
|
+
//# sourceMappingURL=integrations.d.ts.map
|