mage-remote-run 1.3.1 → 1.4.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/bin/mage-remote-run.js +11 -8
- package/lib/commands/orders.js +1 -0
- package/lib/commands/products.js +1 -0
- package/lib/commands/virtual.js +295 -0
- package/lib/mcp.js +4 -0
- package/lib/plugin-loader.js +80 -13
- package/lib/prompts.js +1 -1
- package/package.json +1 -1
package/bin/mage-remote-run.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
|
-
import { loadConfig } from '../lib/config.js';
|
|
4
|
+
import { loadConfig, saveConfig } from '../lib/config.js';
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
|
|
7
7
|
import { createRequire } from 'module';
|
|
@@ -78,20 +78,23 @@ const profile = await getActiveProfile();
|
|
|
78
78
|
// but we can pass a way to get it or just pass null for now if not used at startup.
|
|
79
79
|
// Also mcpServer is not running here unless mcp command is used.
|
|
80
80
|
const appContext = {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
81
|
+
program,
|
|
82
|
+
config: await loadConfig(), // Re-load or reuse config
|
|
83
|
+
saveConfig,
|
|
84
|
+
profile,
|
|
85
|
+
eventBus,
|
|
86
|
+
events,
|
|
87
|
+
createClient
|
|
87
88
|
};
|
|
88
89
|
|
|
89
90
|
const pluginLoader = new PluginLoader(appContext);
|
|
90
91
|
await pluginLoader.loadPlugins();
|
|
91
92
|
|
|
92
93
|
eventBus.emit(events.INIT, appContext);
|
|
94
|
+
import { registerVirtualCommands } from '../lib/commands/virtual.js';
|
|
93
95
|
|
|
94
96
|
registerCommands(program, profile);
|
|
97
|
+
registerVirtualCommands(program, appContext.config, profile);
|
|
95
98
|
|
|
96
99
|
program.hook('preAction', async (thisCommand, actionCommand) => {
|
|
97
100
|
eventBus.emit(events.BEFORE_COMMAND, { thisCommand, actionCommand, profile });
|
|
@@ -113,7 +116,7 @@ program.hook('preAction', async (thisCommand, actionCommand) => {
|
|
|
113
116
|
});
|
|
114
117
|
|
|
115
118
|
program.hook('postAction', async (thisCommand, actionCommand) => {
|
|
116
|
-
|
|
119
|
+
eventBus.emit(events.AFTER_COMMAND, { thisCommand, actionCommand, profile });
|
|
117
120
|
});
|
|
118
121
|
|
|
119
122
|
import { expandCommandAbbreviations } from '../lib/command-helper.js';
|
package/lib/commands/orders.js
CHANGED
|
@@ -32,6 +32,7 @@ Examples:
|
|
|
32
32
|
$ mage-remote-run order list --fields "increment_id,grand_total,customer_email"
|
|
33
33
|
$ mage-remote-run order list --filter "grand_total>=100" --add-fields "base_grand_total,billing_address.city"
|
|
34
34
|
$ mage-remote-run order list --filter "status=pending" --filter "grand_total>100"
|
|
35
|
+
$ mage-remote-run order list --filter "created_at>=2024-01-01"
|
|
35
36
|
$ mage-remote-run order list --sort "grand_total:DESC" --sort "created_at:ASC"
|
|
36
37
|
$ mage-remote-run order list --format json
|
|
37
38
|
`);
|
package/lib/commands/products.js
CHANGED
|
@@ -25,6 +25,7 @@ Examples:
|
|
|
25
25
|
$ mage-remote-run product list --sort-by price --sort-order DESC
|
|
26
26
|
$ mage-remote-run product list --sort "price:DESC" "sku:ASC"
|
|
27
27
|
$ mage-remote-run product list --filter "type_id=simple" "price>=100"
|
|
28
|
+
$ mage-remote-run product list --filter "created_at>=2024-01-01"
|
|
28
29
|
$ mage-remote-run product list --fields "sku,name,price"
|
|
29
30
|
$ mage-remote-run product list --add-fields "created_at,updated_at"
|
|
30
31
|
`);
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { createClient } from '../api/factory.js';
|
|
2
|
+
import { handleError, formatOutput, addFormatOption, addFilterOption, addSortOption, addPaginationOptions, buildSearchCriteria, buildSortCriteria } from '../utils.js';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
|
|
5
|
+
function getInputDefinitions(cmdDef) {
|
|
6
|
+
const definitions = new Map();
|
|
7
|
+
const sources = [
|
|
8
|
+
['parameter', cmdDef.parameter],
|
|
9
|
+
['parameter', cmdDef.parameters],
|
|
10
|
+
['option', cmdDef.option],
|
|
11
|
+
['option', cmdDef.options]
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
for (const [source, group] of sources) {
|
|
15
|
+
if (!group || typeof group !== 'object') {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
for (const [key, definition] of Object.entries(group)) {
|
|
20
|
+
definitions.set(key, {
|
|
21
|
+
...definition,
|
|
22
|
+
_source: source
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return Object.fromEntries(definitions);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function buildOptionFlags(key, definition = {}) {
|
|
31
|
+
if (definition.flags) {
|
|
32
|
+
return definition.flags;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const shortFlag = definition.short
|
|
36
|
+
? `${definition.short.startsWith('-') ? definition.short : `-${definition.short}`}, `
|
|
37
|
+
: '';
|
|
38
|
+
const longFlag = definition.long
|
|
39
|
+
? (definition.long.startsWith('-') ? definition.long : `--${definition.long}`)
|
|
40
|
+
: `--${key}`;
|
|
41
|
+
|
|
42
|
+
if (definition.type === 'boolean') {
|
|
43
|
+
return `${shortFlag}${longFlag}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const argName = definition.argName || 'value';
|
|
47
|
+
const valuePlaceholder = definition.variadic ? `<${argName}...>` : `<${argName}>`;
|
|
48
|
+
return `${shortFlag}${longFlag} ${valuePlaceholder}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function getFlagTokens(flags) {
|
|
52
|
+
return flags
|
|
53
|
+
.split(',')
|
|
54
|
+
.map(flag => flag.trim().split(' ')[0])
|
|
55
|
+
.filter(Boolean);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function hasOption(command, flags) {
|
|
59
|
+
const requestedFlags = new Set(getFlagTokens(flags));
|
|
60
|
+
|
|
61
|
+
return (command.options || []).some(option => {
|
|
62
|
+
const existingFlags = [option.short, option.long, ...(option.flags ? getFlagTokens(option.flags) : [])]
|
|
63
|
+
.filter(Boolean);
|
|
64
|
+
|
|
65
|
+
return existingFlags.some(flag => requestedFlags.has(flag));
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function addCommandOption(command, flags, description, defaultValue, required = false) {
|
|
70
|
+
if (hasOption(command, flags)) {
|
|
71
|
+
return command;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (required) {
|
|
75
|
+
if (defaultValue !== undefined) {
|
|
76
|
+
return command.requiredOption(flags, description, defaultValue);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return command.requiredOption(flags, description);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (defaultValue !== undefined) {
|
|
83
|
+
return command.option(flags, description, defaultValue);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return command.option(flags, description);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function normalizeListValue(value) {
|
|
90
|
+
if (value === undefined || value === null) {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (Array.isArray(value)) {
|
|
95
|
+
return value.filter(item => typeof item === 'string' && item.trim().length > 0);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (typeof value === 'string' && value.trim().length > 0) {
|
|
99
|
+
return [value];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function interpolateTemplateValue(template, values) {
|
|
106
|
+
if (typeof template !== 'string' || template.length === 0) {
|
|
107
|
+
return template;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return template
|
|
111
|
+
.replace(/\$\{([a-zA-Z0-9_]+)\}/g, (match, key) => {
|
|
112
|
+
const value = values[key];
|
|
113
|
+
return value === undefined || value === null ? match : String(value);
|
|
114
|
+
})
|
|
115
|
+
.replace(/\{:\s*([a-zA-Z0-9_]+)\s*\}/g, (match, key) => {
|
|
116
|
+
const value = values[key];
|
|
117
|
+
return value === undefined || value === null ? match : String(value);
|
|
118
|
+
})
|
|
119
|
+
.replace(/:([a-zA-Z0-9_]+)/g, (match, key) => {
|
|
120
|
+
const value = values[key];
|
|
121
|
+
return value === undefined || value === null ? match : String(value);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function registerVirtualCommands(program, config, profile) {
|
|
126
|
+
if (!config || !config.commands || !Array.isArray(config.commands)) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
for (const cmdDef of config.commands) {
|
|
131
|
+
// Validation namespace vs command
|
|
132
|
+
if (!cmdDef.name) {
|
|
133
|
+
if (process.env.DEBUG) {
|
|
134
|
+
console.error(chalk.yellow(`Skipping invalid virtual command definition without name: ${JSON.stringify(cmdDef)}`));
|
|
135
|
+
}
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Filtering by connection_types
|
|
140
|
+
if (cmdDef.connection_types && Array.isArray(cmdDef.connection_types)) {
|
|
141
|
+
if (!profile || !profile.type || !cmdDef.connection_types.includes(profile.type)) {
|
|
142
|
+
continue; // Skip if profile type is not in connection_types
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Command Generation: Find or create subcommands
|
|
147
|
+
let currentCmd = program;
|
|
148
|
+
const parts = cmdDef.name.split(' ');
|
|
149
|
+
|
|
150
|
+
for (let i = 0; i < parts.length; i++) {
|
|
151
|
+
const part = parts[i];
|
|
152
|
+
const existing = currentCmd.commands.find(c => c.name() === part);
|
|
153
|
+
if (existing) {
|
|
154
|
+
currentCmd = existing;
|
|
155
|
+
} else {
|
|
156
|
+
currentCmd = currentCmd.command(part);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (cmdDef.description) {
|
|
161
|
+
currentCmd.description(cmdDef.description);
|
|
162
|
+
} else if (cmdDef.endpoint && cmdDef.method) {
|
|
163
|
+
currentCmd.description(`Virtual command to execute ${cmdDef.method.toUpperCase()} ${cmdDef.endpoint}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (cmdDef.summary) {
|
|
167
|
+
currentCmd.summary(cmdDef.summary);
|
|
168
|
+
} else if (cmdDef.description) {
|
|
169
|
+
// Fallback to description if summary is not explicitly provided
|
|
170
|
+
// We use a shortened version if it's too long
|
|
171
|
+
const summaryText = cmdDef.description.split('\n')[0];
|
|
172
|
+
currentCmd.summary(summaryText.length > 80 ? summaryText.substring(0, 77) + '...' : summaryText);
|
|
173
|
+
} else if (cmdDef.endpoint && cmdDef.method) {
|
|
174
|
+
currentCmd.summary(`${cmdDef.method.toUpperCase()} ${cmdDef.endpoint}`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// It is just a namespace wrapper if there's no endpoint or method
|
|
178
|
+
if (!cmdDef.endpoint || !cmdDef.method) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (!hasOption(currentCmd, '-f, --format <type>')) {
|
|
183
|
+
addFormatOption(currentCmd);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Map parameters to options
|
|
187
|
+
const inputDefinitions = getInputDefinitions(cmdDef);
|
|
188
|
+
const parameterKeys = Object.keys(inputDefinitions);
|
|
189
|
+
for (const key of parameterKeys) {
|
|
190
|
+
const paramDef = inputDefinitions[key];
|
|
191
|
+
const flags = buildOptionFlags(key, paramDef);
|
|
192
|
+
const desc = paramDef.description || `${paramDef._source === 'option' ? 'Option' : 'Parameter'} ${key}`;
|
|
193
|
+
|
|
194
|
+
addCommandOption(currentCmd, flags, desc, paramDef.default, paramDef.required);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Add filter and sort options if enabled
|
|
198
|
+
if (cmdDef.supports_filters !== false) {
|
|
199
|
+
if (!hasOption(currentCmd, '--filter <filters...>')) {
|
|
200
|
+
addFilterOption(currentCmd);
|
|
201
|
+
}
|
|
202
|
+
if (!hasOption(currentCmd, '--sort-by <field>')) {
|
|
203
|
+
addSortOption(currentCmd);
|
|
204
|
+
}
|
|
205
|
+
if (!hasOption(currentCmd, '-p, --page <number>')) {
|
|
206
|
+
addPaginationOptions(currentCmd);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Add action
|
|
211
|
+
currentCmd.action(async (options) => {
|
|
212
|
+
try {
|
|
213
|
+
const client = await createClient();
|
|
214
|
+
const method = cmdDef.method.toUpperCase();
|
|
215
|
+
let requestPath = cmdDef.endpoint;
|
|
216
|
+
|
|
217
|
+
const payload = {};
|
|
218
|
+
let queryParams = {};
|
|
219
|
+
|
|
220
|
+
// Process options based on whether they are in the endpoint path
|
|
221
|
+
for (const key of parameterKeys) {
|
|
222
|
+
const value = options[key];
|
|
223
|
+
if (value === undefined || value === null) {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const placeholder = `:${key}`;
|
|
228
|
+
if (requestPath.includes(placeholder)) {
|
|
229
|
+
requestPath = requestPath.replace(placeholder, encodeURIComponent(value));
|
|
230
|
+
} else {
|
|
231
|
+
if (['POST', 'PUT', 'PATCH'].includes(method)) {
|
|
232
|
+
// Can be mapped to nested objects if needed, but for now simple flat payload
|
|
233
|
+
payload[key] = value;
|
|
234
|
+
} else {
|
|
235
|
+
queryParams[key] = value;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (cmdDef.supports_filters !== false) {
|
|
241
|
+
const predefinedFilters = normalizeListValue(cmdDef.filters ?? cmdDef.filter)
|
|
242
|
+
.map(filter => interpolateTemplateValue(filter, options));
|
|
243
|
+
const mergedFilterOptions = {
|
|
244
|
+
...options,
|
|
245
|
+
filter: [
|
|
246
|
+
...predefinedFilters,
|
|
247
|
+
...normalizeListValue(options.filter)
|
|
248
|
+
]
|
|
249
|
+
};
|
|
250
|
+
const { params: searchParams } = buildSearchCriteria(mergedFilterOptions);
|
|
251
|
+
const { params: sortParams } = buildSortCriteria(options);
|
|
252
|
+
|
|
253
|
+
queryParams = {
|
|
254
|
+
...queryParams,
|
|
255
|
+
...searchParams,
|
|
256
|
+
...sortParams
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Clean up any remaining path placeholders if they had defaults or were optional
|
|
261
|
+
requestPath = requestPath.replace(/:[a-zA-Z0-9_]+/g, '');
|
|
262
|
+
|
|
263
|
+
const reqConfig = {
|
|
264
|
+
headers: {
|
|
265
|
+
'Content-Type': 'application/json'
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
if (options.format === 'json') reqConfig.headers.Accept = 'application/json';
|
|
270
|
+
else if (options.format === 'xml') reqConfig.headers.Accept = 'application/xml';
|
|
271
|
+
|
|
272
|
+
const response = await client.request(
|
|
273
|
+
method,
|
|
274
|
+
requestPath,
|
|
275
|
+
Object.keys(payload).length > 0 ? payload : undefined,
|
|
276
|
+
Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
277
|
+
reqConfig
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
if (formatOutput(options, response)) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (typeof response === 'object') {
|
|
285
|
+
console.log(JSON.stringify(response, null, 2));
|
|
286
|
+
} else {
|
|
287
|
+
console.log(response);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
} catch (error) {
|
|
291
|
+
handleError(error);
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
package/lib/mcp.js
CHANGED
|
@@ -158,6 +158,10 @@ export async function startMcpServer(options) {
|
|
|
158
158
|
let token = options.token || process.env.MAGE_REMOTE_RUN_MCP_TOKEN;
|
|
159
159
|
|
|
160
160
|
if (!token) {
|
|
161
|
+
if (!process.stderr.isTTY) {
|
|
162
|
+
throw new Error('Authentication token is required for HTTP transport in non-TTY environments. Please provide it via --token or MAGE_REMOTE_RUN_MCP_TOKEN environment variable.');
|
|
163
|
+
}
|
|
164
|
+
|
|
161
165
|
token = crypto.randomBytes(16).toString('hex');
|
|
162
166
|
console.error(chalk.yellow(`--------------------------------------------------------------------------------`));
|
|
163
167
|
console.error(chalk.yellow(`MCP Server Authentication Token: `) + chalk.green.bold(token));
|
package/lib/plugin-loader.js
CHANGED
|
@@ -41,28 +41,34 @@ export class PluginLoader {
|
|
|
41
41
|
// 2. Try global node_modules (npm)
|
|
42
42
|
try {
|
|
43
43
|
const globalNpmPath = path.join(globalDirs.npm.packages, pluginName);
|
|
44
|
-
|
|
44
|
+
const npmExists = await fs.promises.access(globalNpmPath).then(() => true).catch(() => false);
|
|
45
|
+
if (npmExists) {
|
|
45
46
|
const pkgJsonPath = path.join(globalNpmPath, 'package.json');
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
const pkgJsonExists = await fs.promises.access(pkgJsonPath).then(() => true).catch(() => false);
|
|
48
|
+
if (pkgJsonExists) {
|
|
49
|
+
const pkgContent = await fs.promises.readFile(pkgJsonPath, 'utf-8');
|
|
50
|
+
const pkg = JSON.parse(pkgContent);
|
|
48
51
|
const mainFile = pkg.main || 'index.js';
|
|
49
52
|
pluginPath = path.join(globalNpmPath, mainFile);
|
|
50
53
|
} else {
|
|
51
|
-
|
|
54
|
+
pluginPath = path.join(globalNpmPath, 'index.js');
|
|
52
55
|
}
|
|
53
56
|
} else {
|
|
54
57
|
// 3. Try global node_modules (yarn)
|
|
55
|
-
|
|
56
|
-
|
|
58
|
+
const globalYarnPath = path.join(globalDirs.yarn.packages, pluginName);
|
|
59
|
+
const yarnExists = await fs.promises.access(globalYarnPath).then(() => true).catch(() => false);
|
|
60
|
+
if (yarnExists) {
|
|
57
61
|
const pkgJsonPath = path.join(globalYarnPath, 'package.json');
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
const pkgJsonExists = await fs.promises.access(pkgJsonPath).then(() => true).catch(() => false);
|
|
63
|
+
if (pkgJsonExists) {
|
|
64
|
+
const pkgContent = await fs.promises.readFile(pkgJsonPath, 'utf-8');
|
|
65
|
+
const pkg = JSON.parse(pkgContent);
|
|
60
66
|
const mainFile = pkg.main || 'index.js';
|
|
61
67
|
pluginPath = path.join(globalYarnPath, mainFile);
|
|
62
68
|
} else {
|
|
63
|
-
|
|
69
|
+
pluginPath = path.join(globalYarnPath, 'index.js');
|
|
64
70
|
}
|
|
65
|
-
|
|
71
|
+
}
|
|
66
72
|
}
|
|
67
73
|
} catch (globalErr) {
|
|
68
74
|
// Ignore global errors, proceed to throw if not found
|
|
@@ -81,18 +87,79 @@ export class PluginLoader {
|
|
|
81
87
|
console.log(chalk.gray(`Loading plugin from: ${pluginUrl}`));
|
|
82
88
|
}
|
|
83
89
|
|
|
84
|
-
|
|
90
|
+
// Find plugin root to load static config
|
|
91
|
+
let currentDir = path.dirname(pluginPath);
|
|
92
|
+
let pluginRoot = currentDir;
|
|
93
|
+
while (currentDir !== path.parse(currentDir).root) {
|
|
94
|
+
const pkgPath = path.join(currentDir, 'package.json');
|
|
95
|
+
const pkgExists = await fs.promises.access(pkgPath).then(() => true).catch(() => false);
|
|
96
|
+
if (pkgExists) {
|
|
97
|
+
pluginRoot = currentDir;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
currentDir = path.dirname(currentDir);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Try to load static configuration
|
|
104
|
+
let staticConfig = null;
|
|
105
|
+
const mageConfigPath = path.join(pluginRoot, 'mage-remote-run.json');
|
|
106
|
+
const pkgPath = path.join(pluginRoot, 'package.json');
|
|
107
|
+
|
|
108
|
+
if (await fs.promises.access(mageConfigPath).then(() => true).catch(() => false)) {
|
|
109
|
+
staticConfig = JSON.parse(await fs.promises.readFile(mageConfigPath, 'utf8'));
|
|
110
|
+
} else if (await fs.promises.access(pkgPath).then(() => true).catch(() => false)) {
|
|
111
|
+
const pkg = JSON.parse(await fs.promises.readFile(pkgPath, 'utf8'));
|
|
112
|
+
if (pkg['mage-remote-run']) {
|
|
113
|
+
staticConfig = pkg['mage-remote-run'];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (staticConfig) {
|
|
118
|
+
if (process.env.DEBUG) {
|
|
119
|
+
console.log(chalk.gray(`Found static configuration for plugin: ${pluginName}`));
|
|
120
|
+
}
|
|
121
|
+
// Merge static config into appContext.config
|
|
122
|
+
if (staticConfig.commands && Array.isArray(staticConfig.commands)) {
|
|
123
|
+
if (!this.appContext.config.commands) {
|
|
124
|
+
this.appContext.config.commands = [];
|
|
125
|
+
}
|
|
126
|
+
this.appContext.config.commands.push(...staticConfig.commands);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
let pluginModule = null;
|
|
131
|
+
try {
|
|
132
|
+
pluginModule = await import(pluginUrl);
|
|
133
|
+
} catch (err) {
|
|
134
|
+
if (process.env.DEBUG) {
|
|
135
|
+
console.error(chalk.yellow(`Could not import plugin module for ${pluginName} at ${pluginUrl}: ${err.message}`));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
85
138
|
|
|
86
139
|
if (pluginModule && pluginModule.default) {
|
|
87
140
|
if (typeof pluginModule.default === 'function') {
|
|
88
141
|
await pluginModule.default(this.appContext);
|
|
89
142
|
this.plugins.push({ name: pluginName, module: pluginModule });
|
|
90
143
|
if (process.env.DEBUG) {
|
|
91
|
-
console.log(chalk.gray(`Loaded plugin: ${pluginName}`));
|
|
144
|
+
console.log(chalk.gray(`Loaded plugin script: ${pluginName}`));
|
|
92
145
|
}
|
|
93
146
|
} else {
|
|
94
|
-
|
|
147
|
+
if (!staticConfig) {
|
|
148
|
+
console.warn(chalk.yellow(`Plugin ${pluginName} does not export a default function and has no static config.`));
|
|
149
|
+
} else {
|
|
150
|
+
this.plugins.push({ name: pluginName, module: pluginModule });
|
|
151
|
+
if (process.env.DEBUG) {
|
|
152
|
+
console.log(chalk.gray(`Loaded plugin config only: ${pluginName}`));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
95
155
|
}
|
|
156
|
+
} else if (staticConfig) {
|
|
157
|
+
this.plugins.push({ name: pluginName, module: null });
|
|
158
|
+
if (process.env.DEBUG) {
|
|
159
|
+
console.log(chalk.gray(`Loaded plugin config only: ${pluginName}`));
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
console.warn(chalk.yellow(`Plugin ${pluginName} could not be loaded because it has no default export and no static config.`));
|
|
96
163
|
}
|
|
97
164
|
}
|
|
98
165
|
}
|
package/lib/prompts.js
CHANGED