mage-remote-run 0.26.1 → 0.28.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 +29 -0
- package/lib/command-registry.js +4 -2
- package/lib/commands/plugins.js +106 -0
- package/lib/config.js +6 -2
- package/lib/events.js +12 -0
- package/lib/mcp.js +33 -40
- package/lib/plugin-loader.js +98 -0
- package/package.json +3 -2
package/bin/mage-remote-run.js
CHANGED
|
@@ -44,6 +44,9 @@ import {
|
|
|
44
44
|
} from '../lib/command-registry.js';
|
|
45
45
|
import { getActiveProfile } from '../lib/config.js';
|
|
46
46
|
import { startMcpServer } from '../lib/mcp.js';
|
|
47
|
+
import { PluginLoader } from '../lib/plugin-loader.js';
|
|
48
|
+
import { eventBus, EVENTS } from '../lib/events.js';
|
|
49
|
+
import { createClient } from '../lib/api/factory.js';
|
|
47
50
|
|
|
48
51
|
// Connection commands are registered dynamically via registerCommands
|
|
49
52
|
// But we need them registered early if we want them to show up in help even if config fails?
|
|
@@ -65,9 +68,31 @@ program.command('mcp [args...]')
|
|
|
65
68
|
});
|
|
66
69
|
|
|
67
70
|
const profile = await getActiveProfile();
|
|
71
|
+
|
|
72
|
+
// Load Plugins
|
|
73
|
+
// We construct an initial context.
|
|
74
|
+
// Note: client is not available yet as it is created per command usually,
|
|
75
|
+
// but we can pass a way to get it or just pass null for now if not used at startup.
|
|
76
|
+
// Also mcpServer is not running here unless mcp command is used.
|
|
77
|
+
const appContext = {
|
|
78
|
+
program,
|
|
79
|
+
config: await loadConfig(), // Re-load or reuse config
|
|
80
|
+
profile,
|
|
81
|
+
eventBus,
|
|
82
|
+
EVENTS,
|
|
83
|
+
createClient
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const pluginLoader = new PluginLoader(appContext);
|
|
87
|
+
await pluginLoader.loadPlugins();
|
|
88
|
+
|
|
89
|
+
eventBus.emit(EVENTS.INIT, appContext);
|
|
90
|
+
|
|
68
91
|
registerCommands(program, profile);
|
|
69
92
|
|
|
70
93
|
program.hook('preAction', async (thisCommand, actionCommand) => {
|
|
94
|
+
eventBus.emit(EVENTS.BEFORE_COMMAND, { thisCommand, actionCommand, profile });
|
|
95
|
+
|
|
71
96
|
// Check if we have an active profile and if format is not json/xml
|
|
72
97
|
// Note: 'options' are available on the command that has them defined.
|
|
73
98
|
// actionCommand is the command actually being executed.
|
|
@@ -84,6 +109,10 @@ program.hook('preAction', async (thisCommand, actionCommand) => {
|
|
|
84
109
|
}
|
|
85
110
|
});
|
|
86
111
|
|
|
112
|
+
program.hook('postAction', async (thisCommand, actionCommand) => {
|
|
113
|
+
eventBus.emit(EVENTS.AFTER_COMMAND, { thisCommand, actionCommand, profile });
|
|
114
|
+
});
|
|
115
|
+
|
|
87
116
|
import { expandCommandAbbreviations } from '../lib/command-helper.js';
|
|
88
117
|
|
|
89
118
|
// Check for first run (no profiles configured and no arguments or just help)
|
package/lib/command-registry.js
CHANGED
|
@@ -17,8 +17,9 @@ import { registerModulesCommands } from './commands/modules.js';
|
|
|
17
17
|
import { registerConsoleCommand } from './commands/console.js';
|
|
18
18
|
import { registerShipmentCommands } from './commands/shipments.js';
|
|
19
19
|
import { registerRestCommands } from './commands/rest.js';
|
|
20
|
+
import { registerPluginsCommands } from './commands/plugins.js';
|
|
20
21
|
|
|
21
|
-
export { registerConnectionCommands, registerConsoleCommand, registerShipmentCommands, registerRestCommands };
|
|
22
|
+
export { registerConnectionCommands, registerConsoleCommand, registerShipmentCommands, registerRestCommands, registerPluginsCommands };
|
|
22
23
|
|
|
23
24
|
const GROUPS = {
|
|
24
25
|
CORE: [
|
|
@@ -33,7 +34,8 @@ const GROUPS = {
|
|
|
33
34
|
registerInventoryCommands,
|
|
34
35
|
registerShipmentCommands,
|
|
35
36
|
registerConsoleCommand,
|
|
36
|
-
registerRestCommands
|
|
37
|
+
registerRestCommands,
|
|
38
|
+
registerPluginsCommands
|
|
37
39
|
],
|
|
38
40
|
COMMERCE: [
|
|
39
41
|
registerCompanyCommands,
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { loadConfig, saveConfig } from '../config.js';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { realpath } from 'node:fs/promises';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
function isFilesystemPath(pluginRef) {
|
|
8
|
+
const isScopedPackageName = /^@[^/\\]+\/[^/\\]+$/.test(pluginRef);
|
|
9
|
+
const hasPathSeparator = pluginRef.includes('/') || pluginRef.includes('\\');
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
path.isAbsolute(pluginRef)
|
|
13
|
+
|| pluginRef.startsWith('./')
|
|
14
|
+
|| pluginRef.startsWith('../')
|
|
15
|
+
|| pluginRef.startsWith('.\\')
|
|
16
|
+
|| pluginRef.startsWith('..\\')
|
|
17
|
+
|| pluginRef.startsWith('~/')
|
|
18
|
+
|| pluginRef.startsWith('~\\')
|
|
19
|
+
|| pluginRef.startsWith('file:')
|
|
20
|
+
|| (hasPathSeparator && !isScopedPackageName)
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function resolvePluginReference(pluginRef) {
|
|
25
|
+
if (!isFilesystemPath(pluginRef)) {
|
|
26
|
+
return pluginRef;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (pluginRef.startsWith('~/') || pluginRef.startsWith('~\\')) {
|
|
30
|
+
return realpath(path.join(process.env.HOME || process.env.USERPROFILE || '', pluginRef.slice(2)));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (pluginRef.startsWith('file:')) {
|
|
34
|
+
return realpath(new URL(pluginRef));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return realpath(pluginRef);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
export function registerPluginsCommands(program) {
|
|
42
|
+
const pluginsCmd = program.command('plugin')
|
|
43
|
+
.description('Manage plugins');
|
|
44
|
+
|
|
45
|
+
pluginsCmd.command('register <package-name>')
|
|
46
|
+
.description('Register an installed plugin in the configuration')
|
|
47
|
+
.action(async (packageName) => {
|
|
48
|
+
try {
|
|
49
|
+
const pluginRef = await resolvePluginReference(packageName);
|
|
50
|
+
const config = await loadConfig();
|
|
51
|
+
if (!config.plugins) {
|
|
52
|
+
config.plugins = [];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (config.plugins.includes(pluginRef)) {
|
|
56
|
+
console.log(chalk.yellow(`Plugin "${pluginRef}" is already registered.`));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
config.plugins.push(pluginRef);
|
|
61
|
+
await saveConfig(config);
|
|
62
|
+
console.log(chalk.green(`Plugin "${pluginRef}" successfully registered.`));
|
|
63
|
+
console.log(chalk.gray(`Make sure the package is installed globally or in the local project.`));
|
|
64
|
+
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error(chalk.red(`Error registering plugin: ${error.message}`));
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
pluginsCmd.command('unregister <package-name>')
|
|
71
|
+
.description('Unregister a plugin from the configuration')
|
|
72
|
+
.action(async (packageName) => {
|
|
73
|
+
try {
|
|
74
|
+
const pluginRef = await resolvePluginReference(packageName);
|
|
75
|
+
const config = await loadConfig();
|
|
76
|
+
if (!config.plugins || !config.plugins.includes(pluginRef)) {
|
|
77
|
+
console.log(chalk.yellow(`Plugin "${pluginRef}" is not registered.`));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
config.plugins = config.plugins.filter(p => p !== pluginRef);
|
|
82
|
+
await saveConfig(config);
|
|
83
|
+
console.log(chalk.green(`Plugin "${pluginRef}" successfully unregistered.`));
|
|
84
|
+
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error(chalk.red(`Error unregistering plugin: ${error.message}`));
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
pluginsCmd.command('list')
|
|
91
|
+
.description('List registered plugins')
|
|
92
|
+
.action(async () => {
|
|
93
|
+
const config = await loadConfig();
|
|
94
|
+
const plugins = config.plugins || [];
|
|
95
|
+
|
|
96
|
+
if (plugins.length === 0) {
|
|
97
|
+
console.log(chalk.gray('No plugins registered.'));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
console.log(chalk.bold('Registered Plugins:'));
|
|
102
|
+
plugins.forEach(plugin => {
|
|
103
|
+
console.log(`- ${plugin}`);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
}
|
package/lib/config.js
CHANGED
|
@@ -42,10 +42,14 @@ export async function loadConfig() {
|
|
|
42
42
|
try {
|
|
43
43
|
await migrateOldConfig();
|
|
44
44
|
if (!fs.existsSync(CONFIG_FILE)) {
|
|
45
|
-
return { profiles: {}, activeProfile: null };
|
|
45
|
+
return { profiles: {}, activeProfile: null, plugins: [] };
|
|
46
46
|
}
|
|
47
47
|
const data = fs.readFileSync(CONFIG_FILE, 'utf-8');
|
|
48
|
-
|
|
48
|
+
const config = JSON.parse(data);
|
|
49
|
+
if (!config.plugins) {
|
|
50
|
+
config.plugins = [];
|
|
51
|
+
}
|
|
52
|
+
return config;
|
|
49
53
|
} catch (e) {
|
|
50
54
|
console.error("Error loading config:", e.message);
|
|
51
55
|
return { profiles: {}, activeProfile: null };
|
package/lib/events.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
|
|
3
|
+
export class AppEventBus extends EventEmitter {}
|
|
4
|
+
|
|
5
|
+
export const eventBus = new AppEventBus();
|
|
6
|
+
|
|
7
|
+
export const EVENTS = {
|
|
8
|
+
INIT: 'init',
|
|
9
|
+
BEFORE_COMMAND: 'beforeCommand',
|
|
10
|
+
AFTER_COMMAND: 'afterCommand',
|
|
11
|
+
MCP_START: 'mcpStart'
|
|
12
|
+
};
|
package/lib/mcp.js
CHANGED
|
@@ -9,6 +9,9 @@ import { readFileSync } from "fs";
|
|
|
9
9
|
|
|
10
10
|
// Import command registry
|
|
11
11
|
import { registerAllCommands } from './command-registry.js';
|
|
12
|
+
import { PluginLoader } from './plugin-loader.js';
|
|
13
|
+
import { loadConfig } from './config.js';
|
|
14
|
+
import { eventBus, AppEventBus, EVENTS } from './events.js';
|
|
12
15
|
|
|
13
16
|
// Helper to strip ANSI codes for cleaner output
|
|
14
17
|
function stripAnsi(str) {
|
|
@@ -28,13 +31,15 @@ export async function startMcpServer(options) {
|
|
|
28
31
|
);
|
|
29
32
|
|
|
30
33
|
// 1. Setup a dynamic program to discovery commands
|
|
31
|
-
const program =
|
|
34
|
+
const program = await setupProgramAsync();
|
|
32
35
|
|
|
33
36
|
const server = new McpServer({
|
|
34
37
|
name: "mage-remote-run",
|
|
35
38
|
version: packageJson.version
|
|
36
39
|
});
|
|
37
40
|
|
|
41
|
+
eventBus.emit(EVENTS.MCP_START, { server, options });
|
|
42
|
+
|
|
38
43
|
const toolsCount = registerTools(server, program);
|
|
39
44
|
|
|
40
45
|
if (options.transport === 'http') {
|
|
@@ -138,7 +143,7 @@ function registerTools(server, program) {
|
|
|
138
143
|
|
|
139
144
|
// Re-register all commands on a fresh program instance
|
|
140
145
|
// We export this logic so we can reuse it
|
|
141
|
-
function
|
|
146
|
+
async function setupProgramAsync() {
|
|
142
147
|
const program = new Command();
|
|
143
148
|
|
|
144
149
|
// Silence output for the main program instance to avoid double printing during parsing
|
|
@@ -147,8 +152,33 @@ function setupProgram() {
|
|
|
147
152
|
writeErr: (str) => { }
|
|
148
153
|
});
|
|
149
154
|
|
|
155
|
+
const localEventBus = new AppEventBus();
|
|
156
|
+
|
|
157
|
+
const appContext = {
|
|
158
|
+
program,
|
|
159
|
+
config: await loadConfig(),
|
|
160
|
+
profile: null, // MCP uses all commands, usually? Or should we use active profile?
|
|
161
|
+
// registerAllCommands registers everything. Plugins might need to know if they should register.
|
|
162
|
+
// Assuming plugins register globally for now or handle checking config themselves.
|
|
163
|
+
eventBus: localEventBus,
|
|
164
|
+
EVENTS
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const pluginLoader = new PluginLoader(appContext);
|
|
168
|
+
await pluginLoader.loadPlugins();
|
|
169
|
+
|
|
170
|
+
localEventBus.emit(EVENTS.INIT, appContext);
|
|
171
|
+
|
|
150
172
|
registerAllCommands(program);
|
|
151
173
|
|
|
174
|
+
program.hook('preAction', async (thisCommand, actionCommand) => {
|
|
175
|
+
localEventBus.emit(EVENTS.BEFORE_COMMAND, { thisCommand, actionCommand, profile: null });
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
program.hook('postAction', async (thisCommand, actionCommand) => {
|
|
179
|
+
localEventBus.emit(EVENTS.AFTER_COMMAND, { thisCommand, actionCommand, profile: null });
|
|
180
|
+
});
|
|
181
|
+
|
|
152
182
|
return program;
|
|
153
183
|
}
|
|
154
184
|
|
|
@@ -172,7 +202,7 @@ async function executeCommand(cmdDefinition, args, parentName) {
|
|
|
172
202
|
console.error = logInterceptor;
|
|
173
203
|
|
|
174
204
|
try {
|
|
175
|
-
const program =
|
|
205
|
+
const program = await setupProgramAsync();
|
|
176
206
|
|
|
177
207
|
// Construct argv
|
|
178
208
|
// We need to build [node, script, command, subcommand, ..., args, options]
|
|
@@ -180,43 +210,6 @@ async function executeCommand(cmdDefinition, args, parentName) {
|
|
|
180
210
|
|
|
181
211
|
// Reconstruct command path
|
|
182
212
|
if (parentName) {
|
|
183
|
-
// parentName might be "website" or "website_domain" (if nested deeper? current logic supports 1 level nesting)
|
|
184
|
-
// Current processCommand logic: `cmdName = parentName ? ${parentName}_${cmd.name()} : cmd.name()`
|
|
185
|
-
// But parentName passed to executeCommand is the prefix.
|
|
186
|
-
// Wait, registerTools calls: `executeCommand(cmd, args, parentName)`
|
|
187
|
-
// If parentName is "website", and cmd is "list", we need "website list"
|
|
188
|
-
|
|
189
|
-
// NOTE: parentName in processCommand is built recursively with underscores?
|
|
190
|
-
// In processCommand(subCmd, cmdName), cmdName is "parent_sub".
|
|
191
|
-
// So if we have website -> list.
|
|
192
|
-
// processCommand(website) -> processCommand(list, "website")
|
|
193
|
-
// parentName in executeCommand is "website".
|
|
194
|
-
// cmd.name() is "list".
|
|
195
|
-
|
|
196
|
-
// However, parentName might contain underscores if deeper nesting?
|
|
197
|
-
// "store_config_list" -> parent "store_config", cmd "list".
|
|
198
|
-
// Commander commands are usually space separated in argv.
|
|
199
|
-
|
|
200
|
-
// We need to parse parentName back to argv tokens?
|
|
201
|
-
// Or better: store the "command path" as an array in the tool context.
|
|
202
|
-
|
|
203
|
-
// Let's rely on standard splitting by underscore, assuming command names don't have underscores.
|
|
204
|
-
// Or we can assume parentName matches the command structure.
|
|
205
|
-
|
|
206
|
-
// Safest: splitting parentName by UNDERSCORE might be risky if command names have underscores.
|
|
207
|
-
// But standard commands here: website, store, etc. don't.
|
|
208
|
-
|
|
209
|
-
// Actually, we can just push parentName, then cmd.name()
|
|
210
|
-
// But parentName comes from `cmdName` variable passed as `parentName` to recursive call.
|
|
211
|
-
// `processCommand(subCmd, cmdName)`
|
|
212
|
-
// `cmdName` = `parentName_cmd.name()`.
|
|
213
|
-
// So for `website list`:
|
|
214
|
-
// `processCommand(website, '')` -> `cmdName="website"`.
|
|
215
|
-
// -> `processCommand(list, "website")`.
|
|
216
|
-
// -> register tool "website_list". `parentName` passed to execute is "website".
|
|
217
|
-
|
|
218
|
-
// So `parentName` is the accumulated prefix with underscores.
|
|
219
|
-
|
|
220
213
|
const parts = parentName.split('_');
|
|
221
214
|
argv.push(...parts);
|
|
222
215
|
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { pathToFileURL } from 'url';
|
|
4
|
+
import globalDirs from 'global-dirs';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { createRequire } from 'module';
|
|
7
|
+
import { loadConfig } from './config.js';
|
|
8
|
+
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
|
|
11
|
+
export class PluginLoader {
|
|
12
|
+
constructor(appContext) {
|
|
13
|
+
this.appContext = appContext;
|
|
14
|
+
this.plugins = [];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async loadPlugins() {
|
|
18
|
+
const config = await loadConfig();
|
|
19
|
+
// If config.plugins is missing, we default to empty array
|
|
20
|
+
const plugins = config.plugins || [];
|
|
21
|
+
|
|
22
|
+
for (const pluginName of plugins) {
|
|
23
|
+
try {
|
|
24
|
+
await this.loadPlugin(pluginName);
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.error(chalk.red(`Failed to load plugin ${pluginName}: ${e.message}`));
|
|
27
|
+
if (process.env.DEBUG) {
|
|
28
|
+
console.error(e);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async loadPlugin(pluginName) {
|
|
35
|
+
let pluginPath;
|
|
36
|
+
|
|
37
|
+
// 1. Try local node_modules
|
|
38
|
+
try {
|
|
39
|
+
pluginPath = require.resolve(pluginName);
|
|
40
|
+
} catch (e) {
|
|
41
|
+
// 2. Try global node_modules (npm)
|
|
42
|
+
try {
|
|
43
|
+
const globalNpmPath = path.join(globalDirs.npm.packages, pluginName);
|
|
44
|
+
if (fs.existsSync(globalNpmPath)) {
|
|
45
|
+
const pkgJsonPath = path.join(globalNpmPath, 'package.json');
|
|
46
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
47
|
+
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
|
|
48
|
+
const mainFile = pkg.main || 'index.js';
|
|
49
|
+
pluginPath = path.join(globalNpmPath, mainFile);
|
|
50
|
+
} else {
|
|
51
|
+
pluginPath = path.join(globalNpmPath, 'index.js');
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
// 3. Try global node_modules (yarn)
|
|
55
|
+
const globalYarnPath = path.join(globalDirs.yarn.packages, pluginName);
|
|
56
|
+
if (fs.existsSync(globalYarnPath)) {
|
|
57
|
+
const pkgJsonPath = path.join(globalYarnPath, 'package.json');
|
|
58
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
59
|
+
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
|
|
60
|
+
const mainFile = pkg.main || 'index.js';
|
|
61
|
+
pluginPath = path.join(globalYarnPath, mainFile);
|
|
62
|
+
} else {
|
|
63
|
+
pluginPath = path.join(globalYarnPath, 'index.js');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
} catch (globalErr) {
|
|
68
|
+
// Ignore global errors, proceed to throw if not found
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!pluginPath) {
|
|
73
|
+
throw new Error(`Could not resolve plugin '${pluginName}' locally or globally.`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Import using file URL for absolute paths in ESM
|
|
77
|
+
// Windows paths need to be converted to file URLs
|
|
78
|
+
const pluginUrl = pathToFileURL(pluginPath).href;
|
|
79
|
+
|
|
80
|
+
if (process.env.DEBUG) {
|
|
81
|
+
console.log(chalk.gray(`Loading plugin from: ${pluginUrl}`));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const pluginModule = await import(pluginUrl);
|
|
85
|
+
|
|
86
|
+
if (pluginModule && pluginModule.default) {
|
|
87
|
+
if (typeof pluginModule.default === 'function') {
|
|
88
|
+
await pluginModule.default(this.appContext);
|
|
89
|
+
this.plugins.push({ name: pluginName, module: pluginModule });
|
|
90
|
+
if (process.env.DEBUG) {
|
|
91
|
+
console.log(chalk.gray(`Loaded plugin: ${pluginName}`));
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
console.warn(chalk.yellow(`Plugin ${pluginName} does not export a default function.`));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mage-remote-run",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "The remote swiss army knife for Magento Open Source, Mage-OS, Adobe Commerce",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"commander": "^14.0.2",
|
|
41
41
|
"csv-parse": "^6.1.0",
|
|
42
42
|
"csv-stringify": "^6.6.0",
|
|
43
|
-
"env-paths": "^
|
|
43
|
+
"env-paths": "^4.0.0",
|
|
44
|
+
"global-dirs": "^3.0.1",
|
|
44
45
|
"html-to-text": "^9.0.5",
|
|
45
46
|
"inquirer": "^13.1.0",
|
|
46
47
|
"mkdirp": "^3.0.1",
|