@qelos/plugins-cli 0.0.10 → 0.0.11
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/commands/pull.mjs +3 -3
- package/commands/push.mjs +3 -3
- package/controllers/pull.mjs +34 -1
- package/controllers/push.mjs +52 -11
- package/package.json +1 -1
- package/services/blocks.mjs +166 -0
- package/services/plugins.mjs +159 -0
package/commands/pull.mjs
CHANGED
|
@@ -2,13 +2,13 @@ import pullController from "../controllers/pull.mjs";
|
|
|
2
2
|
|
|
3
3
|
export default function pullCommand(program) {
|
|
4
4
|
program
|
|
5
|
-
.command('pull [type] [path]', 'pull from qelos app. Ability to pull components, blueprints, configurations, and more.',
|
|
5
|
+
.command('pull [type] [path]', 'pull from qelos app. Ability to pull components, blueprints, configurations, plugins, blocks, and more.',
|
|
6
6
|
(yargs) => {
|
|
7
7
|
return yargs
|
|
8
8
|
.positional('type', {
|
|
9
|
-
describe: 'Type of the resource to pull. Can be components, blueprints, configurations, or
|
|
9
|
+
describe: 'Type of the resource to pull. Can be components, blueprints, configurations, plugins, blocks, or all.',
|
|
10
10
|
type: 'string',
|
|
11
|
-
choices: ['components', 'blueprints', 'configs'],
|
|
11
|
+
choices: ['components', 'blueprints', 'configs', 'plugins', 'blocks', 'all', '*'],
|
|
12
12
|
required: true
|
|
13
13
|
})
|
|
14
14
|
.positional('path', {
|
package/commands/push.mjs
CHANGED
|
@@ -2,13 +2,13 @@ import pushController from "../controllers/push.mjs";
|
|
|
2
2
|
|
|
3
3
|
export default function createCommand(program) {
|
|
4
4
|
program
|
|
5
|
-
.command('push [type] [path]', 'push to qelos app. Ability to push components, blueprints, configurations, and more.',
|
|
5
|
+
.command('push [type] [path]', 'push to qelos app. Ability to push components, blueprints, configurations, plugins, blocks, and more.',
|
|
6
6
|
(yargs) => {
|
|
7
7
|
return yargs
|
|
8
8
|
.positional('type', {
|
|
9
|
-
describe: 'Type of the resource to push. Can be components, blueprints, configurations, or
|
|
9
|
+
describe: 'Type of the resource to push. Can be components, blueprints, configurations, plugins, blocks, or all.',
|
|
10
10
|
type: 'string',
|
|
11
|
-
choices: ['components', 'blueprints', 'configs'],
|
|
11
|
+
choices: ['components', 'blueprints', 'configs', 'plugins', 'blocks', 'all', '*'],
|
|
12
12
|
required: true
|
|
13
13
|
})
|
|
14
14
|
.positional('path', {
|
package/controllers/pull.mjs
CHANGED
|
@@ -2,6 +2,8 @@ import { initializeSdk } from '../services/sdk.mjs';
|
|
|
2
2
|
import { pullComponents } from '../services/components.mjs';
|
|
3
3
|
import { pullBlueprints } from '../services/blueprints.mjs';
|
|
4
4
|
import { pullConfigurations } from '../services/configurations.mjs';
|
|
5
|
+
import { pullPlugins } from '../services/plugins.mjs';
|
|
6
|
+
import { pullBlocks } from '../services/blocks.mjs';
|
|
5
7
|
import { logger } from '../services/logger.mjs';
|
|
6
8
|
import fs from 'node:fs';
|
|
7
9
|
import path from 'node:path';
|
|
@@ -25,17 +27,48 @@ export default async function pullController({ type, path: targetPath }) {
|
|
|
25
27
|
|
|
26
28
|
const sdk = await initializeSdk();
|
|
27
29
|
|
|
30
|
+
// Handle "all" or "*" type
|
|
31
|
+
if (type === 'all' || type === '*') {
|
|
32
|
+
logger.section(`Pulling all resources to ${targetPath}`);
|
|
33
|
+
|
|
34
|
+
const types = [
|
|
35
|
+
{ name: 'components', fn: pullComponents },
|
|
36
|
+
{ name: 'blueprints', fn: pullBlueprints },
|
|
37
|
+
{ name: 'configs', fn: pullConfigurations },
|
|
38
|
+
{ name: 'plugins', fn: pullPlugins },
|
|
39
|
+
{ name: 'blocks', fn: pullBlocks }
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
for (const { name, fn } of types) {
|
|
43
|
+
const typePath = path.join(targetPath, name);
|
|
44
|
+
logger.section(`Pulling ${name} to ${typePath}`);
|
|
45
|
+
try {
|
|
46
|
+
await fn(sdk, typePath);
|
|
47
|
+
logger.success(`Successfully pulled ${name}`);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
logger.error(`Failed to pull ${name}`, error);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
logger.success(`Successfully pulled all resources to ${targetPath}`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
28
57
|
logger.section(`Pulling ${type} to ${targetPath}`);
|
|
29
58
|
|
|
30
59
|
if (type === 'components') {
|
|
31
60
|
await pullComponents(sdk, targetPath);
|
|
32
61
|
} else if (type === 'blueprints') {
|
|
33
62
|
await pullBlueprints(sdk, targetPath);
|
|
63
|
+
} else if (type === 'plugins') {
|
|
64
|
+
await pullPlugins(sdk, targetPath);
|
|
65
|
+
} else if (type === 'blocks') {
|
|
66
|
+
await pullBlocks(sdk, targetPath);
|
|
34
67
|
} else if (type === 'config' || type === 'configs' || type === 'configuration') {
|
|
35
68
|
await pullConfigurations(sdk, targetPath);
|
|
36
69
|
} else {
|
|
37
70
|
logger.error(`Unknown type: ${type}`);
|
|
38
|
-
logger.info('Supported types: components, blueprints, config, configs, configuration');
|
|
71
|
+
logger.info('Supported types: components, blueprints, plugins, blocks, config, configs, configuration, all');
|
|
39
72
|
process.exit(1);
|
|
40
73
|
}
|
|
41
74
|
|
package/controllers/push.mjs
CHANGED
|
@@ -2,38 +2,79 @@ import { initializeSdk } from '../services/sdk.mjs';
|
|
|
2
2
|
import { pushComponents } from '../services/components.mjs';
|
|
3
3
|
import { pushBlueprints } from '../services/blueprints.mjs';
|
|
4
4
|
import { pushConfigurations } from '../services/configurations.mjs';
|
|
5
|
+
import { pushPlugins } from '../services/plugins.mjs';
|
|
6
|
+
import { pushBlocks } from '../services/blocks.mjs';
|
|
5
7
|
import { logger } from '../services/logger.mjs';
|
|
6
8
|
import fs from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
7
10
|
|
|
8
|
-
export default async function pushController({ type, path }) {
|
|
11
|
+
export default async function pushController({ type, path: sourcePath }) {
|
|
9
12
|
try {
|
|
10
13
|
// Validate path exists
|
|
11
|
-
if (!fs.existsSync(
|
|
12
|
-
logger.error(`Path does not exist: ${
|
|
14
|
+
if (!fs.existsSync(sourcePath)) {
|
|
15
|
+
logger.error(`Path does not exist: ${sourcePath}`);
|
|
13
16
|
logger.info('Please provide a valid directory path');
|
|
14
17
|
process.exit(1);
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
// Validate path is a directory
|
|
18
|
-
if (!fs.statSync(
|
|
19
|
-
logger.error(`Path is not a directory: ${
|
|
21
|
+
if (!fs.statSync(sourcePath).isDirectory()) {
|
|
22
|
+
logger.error(`Path is not a directory: ${sourcePath}`);
|
|
20
23
|
logger.info('Please provide a directory path, not a file');
|
|
21
24
|
process.exit(1);
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
const sdk = await initializeSdk();
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
// Handle "all" or "*" type
|
|
30
|
+
if (type === 'all' || type === '*') {
|
|
31
|
+
logger.section(`Pushing all resources from ${sourcePath}`);
|
|
32
|
+
|
|
33
|
+
const types = [
|
|
34
|
+
{ name: 'components', fn: pushComponents },
|
|
35
|
+
{ name: 'blueprints', fn: pushBlueprints },
|
|
36
|
+
{ name: 'configs', fn: pushConfigurations },
|
|
37
|
+
{ name: 'plugins', fn: pushPlugins },
|
|
38
|
+
{ name: 'blocks', fn: pushBlocks }
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
for (const { name, fn } of types) {
|
|
42
|
+
const typePath = path.join(sourcePath, name);
|
|
43
|
+
|
|
44
|
+
// Skip if directory doesn't exist
|
|
45
|
+
if (!fs.existsSync(typePath)) {
|
|
46
|
+
logger.info(`Skipping ${name} (directory not found: ${typePath})`);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
logger.section(`Pushing ${name} from ${typePath}`);
|
|
51
|
+
try {
|
|
52
|
+
await fn(sdk, typePath);
|
|
53
|
+
logger.success(`Successfully pushed ${name}`);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
logger.error(`Failed to push ${name}`, error);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
logger.success(`Successfully pushed all resources from ${sourcePath}`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
logger.section(`Pushing ${type} from ${sourcePath}`);
|
|
27
64
|
|
|
28
65
|
if (type === 'components') {
|
|
29
|
-
await pushComponents(sdk,
|
|
66
|
+
await pushComponents(sdk, sourcePath);
|
|
30
67
|
} else if (type === 'blueprints') {
|
|
31
|
-
await pushBlueprints(sdk,
|
|
32
|
-
} else if (type === '
|
|
33
|
-
await
|
|
68
|
+
await pushBlueprints(sdk, sourcePath);
|
|
69
|
+
} else if (type === 'plugins') {
|
|
70
|
+
await pushPlugins(sdk, sourcePath);
|
|
71
|
+
} else if (type === 'blocks') {
|
|
72
|
+
await pushBlocks(sdk, sourcePath);
|
|
73
|
+
} else if (type === 'config' || type === 'configs' || type === 'configuration') {
|
|
74
|
+
await pushConfigurations(sdk, sourcePath);
|
|
34
75
|
} else {
|
|
35
76
|
logger.error(`Unknown type: ${type}`);
|
|
36
|
-
logger.info('Supported types: components, blueprints, config, configs, configuration');
|
|
77
|
+
logger.info('Supported types: components, blueprints, plugins, blocks, config, configs, configuration, all');
|
|
37
78
|
process.exit(1);
|
|
38
79
|
}
|
|
39
80
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { logger } from './logger.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Convert string to kebab-case
|
|
7
|
+
*/
|
|
8
|
+
function toKebabCase(str) {
|
|
9
|
+
return str
|
|
10
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
11
|
+
.replace(/[\s_]+/g, '-')
|
|
12
|
+
.toLowerCase();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Push blocks from local directory to remote
|
|
17
|
+
* @param {Object} sdk - Initialized SDK instance
|
|
18
|
+
* @param {string} path - Path to blocks directory
|
|
19
|
+
*/
|
|
20
|
+
export async function pushBlocks(sdk, path) {
|
|
21
|
+
const files = fs.readdirSync(path);
|
|
22
|
+
const blockFiles = files.filter(f => f.endsWith('.html'));
|
|
23
|
+
|
|
24
|
+
if (blockFiles.length === 0) {
|
|
25
|
+
logger.warning(`No .html files found in ${path}`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
logger.info(`Found ${blockFiles.length} block(s) to push`);
|
|
30
|
+
let blocksJson = {};
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const jsonPath = join(path, 'blocks.json');
|
|
34
|
+
if (fs.existsSync(jsonPath)) {
|
|
35
|
+
blocksJson = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'));
|
|
36
|
+
}
|
|
37
|
+
} catch (error) {
|
|
38
|
+
logger.debug('No blocks.json found or invalid format');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const existingBlocks = await sdk.blocks.getList();
|
|
42
|
+
const updatedBlocksJson = { ...blocksJson };
|
|
43
|
+
|
|
44
|
+
await Promise.all(blockFiles.map(async (file) => {
|
|
45
|
+
const fileName = file.replace('.html', '');
|
|
46
|
+
const info = blocksJson[fileName] || {};
|
|
47
|
+
const content = fs.readFileSync(join(path, file), 'utf-8');
|
|
48
|
+
|
|
49
|
+
logger.step(`Pushing block: ${fileName}`);
|
|
50
|
+
|
|
51
|
+
// First check if we have an _id in blocks.json
|
|
52
|
+
let blockId = info._id;
|
|
53
|
+
|
|
54
|
+
// If we have an _id, try to update directly
|
|
55
|
+
if (blockId) {
|
|
56
|
+
try {
|
|
57
|
+
await sdk.blocks.update(blockId, {
|
|
58
|
+
name: info.name || fileName,
|
|
59
|
+
content,
|
|
60
|
+
contentType: info.contentType || 'html',
|
|
61
|
+
description: info.description || 'Block description'
|
|
62
|
+
});
|
|
63
|
+
logger.success(`Updated: ${fileName}`);
|
|
64
|
+
return;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
// If update fails, the block might have been deleted, so we'll create a new one
|
|
67
|
+
logger.debug(`Block ${fileName} not found by _id, will search or create`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Try to find existing block by name
|
|
72
|
+
const existingBlock = existingBlocks.find(
|
|
73
|
+
block => toKebabCase(block.name) === fileName
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
if (existingBlock) {
|
|
77
|
+
await sdk.blocks.update(existingBlock._id, {
|
|
78
|
+
name: info.name || existingBlock.name,
|
|
79
|
+
content,
|
|
80
|
+
contentType: info.contentType || 'html',
|
|
81
|
+
description: info.description || existingBlock.description || 'Block description'
|
|
82
|
+
});
|
|
83
|
+
updatedBlocksJson[fileName] = {
|
|
84
|
+
...info,
|
|
85
|
+
_id: existingBlock._id
|
|
86
|
+
};
|
|
87
|
+
logger.success(`Updated: ${fileName}`);
|
|
88
|
+
} else {
|
|
89
|
+
const newBlock = await sdk.blocks.create({
|
|
90
|
+
name: info.name || fileName,
|
|
91
|
+
content,
|
|
92
|
+
contentType: info.contentType || 'html',
|
|
93
|
+
description: info.description || 'Block description'
|
|
94
|
+
});
|
|
95
|
+
updatedBlocksJson[fileName] = {
|
|
96
|
+
...info,
|
|
97
|
+
_id: newBlock._id,
|
|
98
|
+
name: newBlock.name
|
|
99
|
+
};
|
|
100
|
+
logger.success(`Created: ${fileName}`);
|
|
101
|
+
}
|
|
102
|
+
}));
|
|
103
|
+
|
|
104
|
+
// Update blocks.json with new _ids
|
|
105
|
+
fs.writeFileSync(
|
|
106
|
+
join(path, 'blocks.json'),
|
|
107
|
+
JSON.stringify(updatedBlocksJson, null, 2)
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
logger.info(`Pushed ${blockFiles.length} block(s)`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Pull blocks from remote to local directory
|
|
115
|
+
* @param {Object} sdk - Initialized SDK instance
|
|
116
|
+
* @param {string} targetPath - Path to save blocks
|
|
117
|
+
*/
|
|
118
|
+
export async function pullBlocks(sdk, targetPath) {
|
|
119
|
+
// Create directory if it doesn't exist
|
|
120
|
+
if (!fs.existsSync(targetPath)) {
|
|
121
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
122
|
+
logger.info(`Created directory: ${targetPath}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const blocks = await sdk.blocks.getList();
|
|
126
|
+
|
|
127
|
+
if (blocks.length === 0) {
|
|
128
|
+
logger.warning('No blocks found to pull');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
logger.info(`Found ${blocks.length} block(s) to pull`);
|
|
133
|
+
|
|
134
|
+
const blocksInformation = await Promise.all(blocks.map(async (block) => {
|
|
135
|
+
const fileName = toKebabCase(block.name);
|
|
136
|
+
const filePath = join(targetPath, `${fileName}.html`);
|
|
137
|
+
|
|
138
|
+
const blockDetails = await sdk.blocks.getBlock(block._id);
|
|
139
|
+
|
|
140
|
+
fs.writeFileSync(filePath, blockDetails.content, 'utf-8');
|
|
141
|
+
logger.step(`Pulled: ${block.name}`);
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
_id: block._id,
|
|
145
|
+
name: block.name,
|
|
146
|
+
description: blockDetails.description,
|
|
147
|
+
contentType: blockDetails.contentType,
|
|
148
|
+
};
|
|
149
|
+
}));
|
|
150
|
+
|
|
151
|
+
fs.writeFileSync(
|
|
152
|
+
join(targetPath, 'blocks.json'),
|
|
153
|
+
JSON.stringify(
|
|
154
|
+
blocksInformation.reduce((obj, current) => {
|
|
155
|
+
const fileName = toKebabCase(current.name);
|
|
156
|
+
obj[fileName] = current;
|
|
157
|
+
return obj;
|
|
158
|
+
}, {}),
|
|
159
|
+
null,
|
|
160
|
+
2
|
|
161
|
+
)
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
logger.info(`Saved blocks.json with metadata`);
|
|
165
|
+
logger.info(`Pulled ${blocks.length} block(s)`);
|
|
166
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { logger } from './logger.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Push plugins from local directory to remote
|
|
7
|
+
* @param {Object} sdk - Initialized SDK instance
|
|
8
|
+
* @param {string} path - Path to plugins directory
|
|
9
|
+
*/
|
|
10
|
+
export async function pushPlugins(sdk, path) {
|
|
11
|
+
const files = fs.readdirSync(path);
|
|
12
|
+
const pluginFiles = files.filter(f => f.endsWith('.plugin.json'));
|
|
13
|
+
|
|
14
|
+
if (pluginFiles.length === 0) {
|
|
15
|
+
logger.warning(`No plugin files (*.plugin.json) found in ${path}`);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
logger.info(`Found ${pluginFiles.length} plugin(s) to push`);
|
|
20
|
+
const existingPlugins = await sdk.managePlugins.getList();
|
|
21
|
+
|
|
22
|
+
const results = await Promise.allSettled(pluginFiles.map(async (file) => {
|
|
23
|
+
if (file.endsWith('.plugin.json')) {
|
|
24
|
+
let pluginData;
|
|
25
|
+
try {
|
|
26
|
+
pluginData = JSON.parse(fs.readFileSync(join(path, file), 'utf-8'));
|
|
27
|
+
} catch (error) {
|
|
28
|
+
logger.error(`Failed to parse ${file}`, error);
|
|
29
|
+
throw new Error(`Parse error in ${file}: ${error.message}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const apiPath = pluginData.apiPath;
|
|
33
|
+
|
|
34
|
+
if (!apiPath) {
|
|
35
|
+
logger.warning(`Skipping ${file}: missing apiPath field`);
|
|
36
|
+
return { skipped: true, file };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
logger.step(`Pushing plugin: ${apiPath}`);
|
|
40
|
+
|
|
41
|
+
const existingPlugin = existingPlugins.find(
|
|
42
|
+
plugin => plugin.apiPath === apiPath
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
if (existingPlugin) {
|
|
47
|
+
await sdk.managePlugins.update(existingPlugin._id, pluginData);
|
|
48
|
+
logger.success(`Updated: ${apiPath}`);
|
|
49
|
+
} else {
|
|
50
|
+
await sdk.managePlugins.create(pluginData);
|
|
51
|
+
logger.success(`Created: ${apiPath}`);
|
|
52
|
+
}
|
|
53
|
+
return { success: true, apiPath };
|
|
54
|
+
} catch (error) {
|
|
55
|
+
// Extract detailed error information
|
|
56
|
+
let errorMessage = error.message || 'Unknown error';
|
|
57
|
+
let errorDetails = null;
|
|
58
|
+
|
|
59
|
+
// The SDK throws the response body as the error
|
|
60
|
+
if (typeof error === 'object' && error !== null) {
|
|
61
|
+
if (error.message) {
|
|
62
|
+
errorMessage = error.message;
|
|
63
|
+
}
|
|
64
|
+
if (error.error) {
|
|
65
|
+
errorDetails = error.error;
|
|
66
|
+
}
|
|
67
|
+
if (error.errors) {
|
|
68
|
+
errorDetails = error.errors;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
logger.error(`Failed to push ${apiPath}: ${errorMessage}`);
|
|
73
|
+
|
|
74
|
+
if (errorDetails) {
|
|
75
|
+
if (typeof errorDetails === 'string') {
|
|
76
|
+
logger.error(` Details: ${errorDetails}`);
|
|
77
|
+
} else {
|
|
78
|
+
logger.error(` Details: ${JSON.stringify(errorDetails, null, 2)}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (process.env.VERBOSE && error.stack) {
|
|
83
|
+
logger.debug(`Stack: ${error.stack}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
throw new Error(`Failed to push ${apiPath}: ${errorMessage}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}));
|
|
90
|
+
|
|
91
|
+
// Check for failures
|
|
92
|
+
const failures = results.filter(r => r.status === 'rejected');
|
|
93
|
+
|
|
94
|
+
if (failures.length > 0) {
|
|
95
|
+
logger.error(`\n${failures.length} plugin(s) failed to push:`);
|
|
96
|
+
failures.forEach(f => {
|
|
97
|
+
logger.error(` • ${f.reason.message}`);
|
|
98
|
+
});
|
|
99
|
+
throw new Error(`Failed to push ${failures.length} plugin(s)`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
logger.info(`Pushed ${pluginFiles.length} plugin(s)`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Pull plugins from remote to local directory
|
|
107
|
+
* @param {Object} sdk - Initialized SDK instance
|
|
108
|
+
* @param {string} targetPath - Path to save plugins
|
|
109
|
+
*/
|
|
110
|
+
export async function pullPlugins(sdk, targetPath) {
|
|
111
|
+
// Create directory if it doesn't exist
|
|
112
|
+
if (!fs.existsSync(targetPath)) {
|
|
113
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
114
|
+
logger.info(`Created directory: ${targetPath}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const plugins = await sdk.managePlugins.getList();
|
|
118
|
+
|
|
119
|
+
if (plugins.length === 0) {
|
|
120
|
+
logger.warning('No plugins found to pull');
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
logger.info(`Found ${plugins.length} plugin(s) to pull`);
|
|
125
|
+
|
|
126
|
+
await Promise.all(plugins.map(async (plugin) => {
|
|
127
|
+
const fileName = `${plugin.apiPath}.plugin.json`;
|
|
128
|
+
const filePath = join(targetPath, fileName);
|
|
129
|
+
|
|
130
|
+
// Fetch full plugin details
|
|
131
|
+
const fullPlugin = await sdk.managePlugins.getById(plugin._id);
|
|
132
|
+
|
|
133
|
+
function removeIdFromObject(obj) {
|
|
134
|
+
const { _id, ...rest } = obj;
|
|
135
|
+
return rest;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const relevantFields = {
|
|
139
|
+
name: fullPlugin.name,
|
|
140
|
+
description: fullPlugin.description,
|
|
141
|
+
manifestUrl: fullPlugin.manifestUrl,
|
|
142
|
+
callbackUrl: fullPlugin.callbackUrl,
|
|
143
|
+
registerUrl: fullPlugin.registerUrl,
|
|
144
|
+
apiPath: fullPlugin.apiPath,
|
|
145
|
+
authAcquire: fullPlugin.authAcquire,
|
|
146
|
+
proxyUrl: fullPlugin.proxyUrl,
|
|
147
|
+
subscribedEvents: (fullPlugin.subscribedEvents || []).map(removeIdFromObject),
|
|
148
|
+
microFrontends: (fullPlugin.microFrontends || []).map(removeIdFromObject),
|
|
149
|
+
injectables: (fullPlugin.injectables || []).map(removeIdFromObject),
|
|
150
|
+
navBarGroups: (fullPlugin.navBarGroups || []).map(removeIdFromObject),
|
|
151
|
+
cruds: (fullPlugin.cruds || []).map(removeIdFromObject),
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
fs.writeFileSync(filePath, JSON.stringify(relevantFields, null, 2), 'utf-8');
|
|
155
|
+
logger.step(`Pulled: ${plugin.apiPath}`);
|
|
156
|
+
}));
|
|
157
|
+
|
|
158
|
+
logger.info(`Pulled ${plugins.length} plugin(s)`);
|
|
159
|
+
}
|